From 010984d4617f40d6aaf99ceaa9ea6184a9dc943c Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 26 Mar 2026 05:55:00 +0000 Subject: [PATCH] Initial commit: Weedops dev tooling Linting configs (PHPCS, ESLint, Stylelint), Forgejo CI pipeline, WordPress health check, PHP linter, strain migration tool, and Docker local dev environment. Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 17 +++++ README.md | 31 ++++++++ ci/forgejo-ci.yml | 88 +++++++++++++++++++++ docker/.env.example | 5 ++ docker/docker-compose.yml | 70 +++++++++++++++++ linting/.eslintrc.json | 30 ++++++++ linting/.phpcs.xml | 45 +++++++++++ linting/.stylelintrc.json | 16 ++++ scripts/lint-php.sh | 84 ++++++++++++++++++++ scripts/run-ci-local.sh | 101 ++++++++++++++++++++++++ scripts/wp-health-check.sh | 139 ++++++++++++++++++++++++++++++++++ scripts/wp-migrate-strains.sh | 99 ++++++++++++++++++++++++ 12 files changed, 725 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 ci/forgejo-ci.yml create mode 100644 docker/.env.example create mode 100644 docker/docker-compose.yml create mode 100644 linting/.eslintrc.json create mode 100644 linting/.phpcs.xml create mode 100644 linting/.stylelintrc.json create mode 100755 scripts/lint-php.sh create mode 100755 scripts/run-ci-local.sh create mode 100755 scripts/wp-health-check.sh create mode 100755 scripts/wp-migrate-strains.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3de1393 --- /dev/null +++ b/.gitignore @@ -0,0 +1,17 @@ +# Environment +.env +docker/.env + +# Dependencies +node_modules/ +vendor/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..39dffc1 --- /dev/null +++ b/README.md @@ -0,0 +1,31 @@ +# Weedops Dev Tooling + +Shared development tools, CI configurations, and scripts for the Weedops WordPress platform. + +## Contents + +- `linting/` — PHP_CodeSniffer and ESLint configs for WordPress theme development +- `ci/` — Forgejo Actions CI pipeline templates +- `scripts/` — Health checks, migration tools, and deployment helpers +- `docker/` — Local development environment configs + +## Usage + +Copy configs into your project or reference them from your CI pipeline: + +```bash +# Run PHP linting +./scripts/lint-php.sh /path/to/theme + +# Run WordPress health check +./scripts/wp-health-check.sh + +# Run full CI locally +./scripts/run-ci-local.sh +``` + +## Standards + +- WordPress Coding Standards for PHP +- ESLint with WordPress preset for JS +- All themes must pass linting before merge diff --git a/ci/forgejo-ci.yml b/ci/forgejo-ci.yml new file mode 100644 index 0000000..eb089f2 --- /dev/null +++ b/ci/forgejo-ci.yml @@ -0,0 +1,88 @@ +# Forgejo Actions CI Pipeline for Weedops WordPress Themes/Plugins +# Place in .forgejo/workflows/ of your repo + +name: Weedops CI + +on: + push: + branches: [main, develop] + pull_request: + branches: [main] + +jobs: + lint-php: + name: PHP Linting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.2' + tools: composer, cs2pr + + - name: Install dependencies + run: | + composer global require wp-coding-standards/wpcs + composer global require phpcompatibility/phpcompatibility-wp + phpcs --config-set installed_paths $(composer global config home)/vendor/wp-coding-standards/wpcs + + - name: Run PHPCS + run: phpcs --standard=WordPress --extensions=php --ignore=vendor,node_modules . + + lint-js: + name: JavaScript Linting + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install ESLint + run: npm install -g eslint + + - name: Run ESLint + run: eslint --ext .js assets/ js/ 2>/dev/null || true + + theme-check: + name: WordPress Theme Check + runs-on: ubuntu-latest + needs: [lint-php] + steps: + - uses: actions/checkout@v4 + + - name: Validate style.css header + run: | + if [ -f style.css ]; then + echo "Checking style.css theme header..." + grep -q "Theme Name:" style.css || (echo "ERROR: Missing Theme Name" && exit 1) + grep -q "Text Domain:" style.css || (echo "ERROR: Missing Text Domain" && exit 1) + echo "Theme header OK" + fi + + - name: Check required template files + run: | + for file in index.php style.css; do + if [ ! -f "$file" ]; then + echo "ERROR: Missing required file: $file" + exit 1 + fi + done + echo "Required files present" + + deploy-staging: + name: Deploy to Staging + runs-on: ubuntu-latest + needs: [lint-php, lint-js, theme-check] + if: github.ref == 'refs/heads/develop' + steps: + - uses: actions/checkout@v4 + + - name: Deploy via rsync + run: | + echo "Deploy to staging would run here" + echo "Target: weedops.site staging environment" diff --git a/docker/.env.example b/docker/.env.example new file mode 100644 index 0000000..f35c067 --- /dev/null +++ b/docker/.env.example @@ -0,0 +1,5 @@ +# Weedops Local Dev Environment Variables +# Copy this to .env and customize + +DB_PASSWORD=weedops_dev +DB_ROOT_PASSWORD=root_dev diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml new file mode 100644 index 0000000..89e539d --- /dev/null +++ b/docker/docker-compose.yml @@ -0,0 +1,70 @@ +# Weedops Local Development Environment +# Usage: docker-compose up -d + +version: '3.8' + +services: + wordpress: + image: wordpress:6.4-php8.2-apache + container_name: weedops-wp + ports: + - "8080:80" + environment: + WORDPRESS_DB_HOST: db + WORDPRESS_DB_USER: weedops + WORDPRESS_DB_PASSWORD: ${DB_PASSWORD:-weedops_dev} + WORDPRESS_DB_NAME: weedops + WORDPRESS_DEBUG: 1 + WORDPRESS_CONFIG_EXTRA: | + define('WP_DEBUG_LOG', true); + define('WP_DEBUG_DISPLAY', true); + define('SCRIPT_DEBUG', true); + volumes: + - wordpress_data:/var/www/html + - ../../../weedops-theme:/var/www/html/wp-content/themes/weedops + depends_on: + - db + restart: unless-stopped + + db: + image: mariadb:10.11 + container_name: weedops-db + environment: + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-root_dev} + MYSQL_DATABASE: weedops + MYSQL_USER: weedops + MYSQL_PASSWORD: ${DB_PASSWORD:-weedops_dev} + volumes: + - db_data:/var/lib/mysql + ports: + - "3307:3306" + restart: unless-stopped + + phpmyadmin: + image: phpmyadmin:latest + container_name: weedops-pma + ports: + - "8081:80" + environment: + PMA_HOST: db + PMA_USER: weedops + PMA_PASSWORD: ${DB_PASSWORD:-weedops_dev} + depends_on: + - db + restart: unless-stopped + + wpcli: + image: wordpress:cli-php8.2 + container_name: weedops-cli + volumes: + - wordpress_data:/var/www/html + - ../../../weedops-theme:/var/www/html/wp-content/themes/weedops + depends_on: + - db + - wordpress + entrypoint: wp + command: "--info" + +volumes: + wordpress_data: + db_data: diff --git a/linting/.eslintrc.json b/linting/.eslintrc.json new file mode 100644 index 0000000..8d259c7 --- /dev/null +++ b/linting/.eslintrc.json @@ -0,0 +1,30 @@ +{ + "env": { + "browser": true, + "es2021": true, + "jquery": true + }, + "extends": [ + "eslint:recommended" + ], + "parserOptions": { + "ecmaVersion": "latest", + "sourceType": "module" + }, + "globals": { + "wp": "readonly", + "ajaxurl": "readonly", + "weedopsData": "readonly" + }, + "rules": { + "no-console": "warn", + "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }], + "prefer-const": "error", + "no-var": "error", + "eqeqeq": ["error", "always"], + "curly": ["error", "all"], + "indent": ["error", "tab"], + "quotes": ["error", "single", { "allowTemplateLiterals": true }], + "semi": ["error", "always"] + } +} diff --git a/linting/.phpcs.xml b/linting/.phpcs.xml new file mode 100644 index 0000000..0fba8af --- /dev/null +++ b/linting/.phpcs.xml @@ -0,0 +1,45 @@ + + + PHP_CodeSniffer ruleset for Weedops WordPress themes and plugins. + + + + + + + + + . + + + */vendor/* + */node_modules/* + */.git/* + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/linting/.stylelintrc.json b/linting/.stylelintrc.json new file mode 100644 index 0000000..6102598 --- /dev/null +++ b/linting/.stylelintrc.json @@ -0,0 +1,16 @@ +{ + "extends": "stylelint-config-wordpress", + "rules": { + "indentation": "tab", + "string-quotes": "double", + "selector-class-pattern": null, + "no-descending-specificity": null, + "font-family-no-missing-generic-family-keyword": true, + "declaration-no-important": true, + "max-nesting-depth": 3 + }, + "ignoreFiles": [ + "vendor/**", + "node_modules/**" + ] +} diff --git a/scripts/lint-php.sh b/scripts/lint-php.sh new file mode 100755 index 0000000..cfe10ae --- /dev/null +++ b/scripts/lint-php.sh @@ -0,0 +1,84 @@ +#!/bin/bash +# PHP Linting Script for Weedops Projects +# Usage: ./lint-php.sh [path-to-check] + +set -euo pipefail + +TARGET="${1:-.}" +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +PHPCS_CONFIG="$SCRIPT_DIR/../linting/.phpcs.xml" + +echo "Weedops PHP Linter" +echo "===================" +echo "Target: $TARGET" +echo "" + +# Check for PHP syntax errors first +echo "--- PHP Syntax Check ---" +SYNTAX_ERRORS=0 +while IFS= read -r -d '' file; do + if ! php -l "$file" &>/dev/null; then + echo "SYNTAX ERROR: $file" + php -l "$file" 2>&1 | tail -1 + ((SYNTAX_ERRORS++)) + fi +done < <(find "$TARGET" -name "*.php" -not -path "*/vendor/*" -not -path "*/node_modules/*" -print0) + +if [ "$SYNTAX_ERRORS" -eq 0 ]; then + echo "All PHP files pass syntax check." +else + echo "" + echo "Found $SYNTAX_ERRORS file(s) with syntax errors!" + exit 1 +fi + +echo "" + +# Run PHPCS if available +echo "--- WordPress Coding Standards ---" +if command -v phpcs &> /dev/null; then + if [ -f "$PHPCS_CONFIG" ]; then + phpcs --standard="$PHPCS_CONFIG" "$TARGET" || true + else + phpcs --standard=WordPress --extensions=php --ignore=vendor,node_modules "$TARGET" || true + fi +else + echo "phpcs not found. Install with: composer global require squizlabs/php_codesniffer" + echo "Running basic checks instead..." + echo "" + + # Basic checks without PHPCS + echo "Checking for common issues..." + ISSUES=0 + + # Check for short PHP tags + SHORT_TAGS=$(grep -rn '/dev/null | grep -v "query\|mysql_query\|mysqli_query' "$TARGET" --include="*.php" 2>/dev/null || true) + if [ -n "$DIRECT_DB" ]; then + echo "WARNING: Direct database queries found (use prepared statements):" + echo "$DIRECT_DB" + ((ISSUES++)) + fi + + # Check for eval usage + EVAL_USAGE=$(grep -rn '\beval\s*(' "$TARGET" --include="*.php" 2>/dev/null || true) + if [ -n "$EVAL_USAGE" ]; then + echo "WARNING: eval() usage found:" + echo "$EVAL_USAGE" + ((ISSUES++)) + fi + + if [ "$ISSUES" -eq 0 ]; then + echo "No common issues found." + fi +fi + +echo "" +echo "Done." diff --git a/scripts/run-ci-local.sh b/scripts/run-ci-local.sh new file mode 100755 index 0000000..474fbca --- /dev/null +++ b/scripts/run-ci-local.sh @@ -0,0 +1,101 @@ +#!/bin/bash +# Run CI checks locally before pushing +# Usage: ./run-ci-local.sh [path-to-theme] + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +TARGET="${1:-.}" + +echo "=========================================" +echo " Weedops Local CI Runner" +echo " $(date '+%Y-%m-%d %H:%M:%S')" +echo "=========================================" +echo "" + +TOTAL_PASS=0 +TOTAL_FAIL=0 + +run_check() { + local name="$1" + local cmd="$2" + echo "--- $name ---" + if eval "$cmd"; then + echo "✓ $name passed" + ((TOTAL_PASS++)) + else + echo "✗ $name failed" + ((TOTAL_FAIL++)) + fi + echo "" +} + +# 1. PHP Syntax +run_check "PHP Syntax" "$SCRIPT_DIR/lint-php.sh $TARGET" + +# 2. Theme structure validation +echo "--- Theme Structure ---" +REQUIRED_FILES=("style.css" "index.php") +RECOMMENDED_FILES=("functions.php" "header.php" "footer.php" "screenshot.png") + +ALL_PRESENT=true +for f in "${REQUIRED_FILES[@]}"; do + if [ ! -f "$TARGET/$f" ]; then + echo "MISSING (required): $f" + ALL_PRESENT=false + fi +done + +for f in "${RECOMMENDED_FILES[@]}"; do + if [ ! -f "$TARGET/$f" ]; then + echo "MISSING (recommended): $f" + fi +done + +if $ALL_PRESENT; then + echo "✓ Theme structure OK" + ((TOTAL_PASS++)) +else + echo "✗ Theme structure incomplete" + ((TOTAL_FAIL++)) +fi +echo "" + +# 3. WordPress text domain check +echo "--- Text Domain ---" +MISSING_DOMAIN=$(grep -rn "__(\|_e(\|esc_html__(\|esc_html_e(\|esc_attr__(\|esc_attr_e(" "$TARGET" --include="*.php" 2>/dev/null | grep -v "'weedops'" | grep -v '"weedops"' || true) +if [ -z "$MISSING_DOMAIN" ]; then + echo "✓ Text domain 'weedops' used consistently" + ((TOTAL_PASS++)) +else + echo "WARNING: Some i18n strings may use wrong text domain:" + echo "$MISSING_DOMAIN" | head -5 + ((TOTAL_PASS++)) # Warning, not failure +fi +echo "" + +# 4. Security basics +echo "--- Security Checks ---" +SECURITY_OK=true + +# Check for direct file access prevention +PHP_FILES=$(find "$TARGET" -name "*.php" -not -path "*/vendor/*" 2>/dev/null) +for f in $PHP_FILES; do + if ! grep -q "ABSPATH\|defined(" "$f" 2>/dev/null; then + BASENAME=$(basename "$f") + if [ "$BASENAME" != "index.php" ] && [ "$BASENAME" != "style.css" ]; then + echo "WARNING: $f may lack direct access prevention" + fi + fi +done + +echo "✓ Security check complete" +((TOTAL_PASS++)) +echo "" + +# Summary +echo "=========================================" +echo " Results: $TOTAL_PASS passed, $TOTAL_FAIL failed" +echo "=========================================" + +[ "$TOTAL_FAIL" -eq 0 ] && exit 0 || exit 1 diff --git a/scripts/wp-health-check.sh b/scripts/wp-health-check.sh new file mode 100755 index 0000000..4d7c4b0 --- /dev/null +++ b/scripts/wp-health-check.sh @@ -0,0 +1,139 @@ +#!/bin/bash +# Weedops WordPress Health Check Script +# Checks site availability, database, plugins, and theme status +# Usage: ./wp-health-check.sh [site-url] + +set -euo pipefail + +SITE_URL="${1:-https://weedops.site}" +WP_PATH="${2:-/var/www/weedops}" +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' + +PASS=0 +FAIL=0 +WARN=0 + +check_pass() { echo -e "${GREEN}[PASS]${NC} $1"; ((PASS++)); } +check_fail() { echo -e "${RED}[FAIL]${NC} $1"; ((FAIL++)); } +check_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; ((WARN++)); } + +echo "=========================================" +echo " Weedops WordPress Health Check" +echo " $(date '+%Y-%m-%d %H:%M:%S')" +echo "=========================================" +echo "" + +# 1. HTTP Response Check +echo "--- Site Availability ---" +HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$SITE_URL" 2>/dev/null || echo "000") +if [ "$HTTP_CODE" = "200" ]; then + check_pass "Site responding with HTTP $HTTP_CODE" +elif [ "$HTTP_CODE" = "301" ] || [ "$HTTP_CODE" = "302" ]; then + check_warn "Site redirecting with HTTP $HTTP_CODE" +else + check_fail "Site returned HTTP $HTTP_CODE" +fi + +# SSL Check +if echo "$SITE_URL" | grep -q "https"; then + SSL_EXPIRY=$(echo | openssl s_client -servername "$(echo "$SITE_URL" | sed 's|https://||')" -connect "$(echo "$SITE_URL" | sed 's|https://||')":443 2>/dev/null | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2) + if [ -n "$SSL_EXPIRY" ]; then + check_pass "SSL certificate valid until: $SSL_EXPIRY" + else + check_warn "Could not verify SSL certificate" + fi +fi + +echo "" + +# 2. WP-CLI Checks (if available) +echo "--- WordPress Core ---" +if command -v wp &> /dev/null && [ -d "$WP_PATH" ]; then + WP_VERSION=$(wp core version --path="$WP_PATH" 2>/dev/null || echo "unknown") + if [ "$WP_VERSION" != "unknown" ]; then + check_pass "WordPress version: $WP_VERSION" + else + check_warn "Could not determine WordPress version" + fi + + # Check for updates + UPDATE_COUNT=$(wp core check-update --path="$WP_PATH" --format=count 2>/dev/null || echo "0") + if [ "$UPDATE_COUNT" = "0" ]; then + check_pass "WordPress core is up to date" + else + check_warn "WordPress core update available" + fi + + # Active theme + ACTIVE_THEME=$(wp theme list --path="$WP_PATH" --status=active --field=name 2>/dev/null || echo "unknown") + check_pass "Active theme: $ACTIVE_THEME" + + # Plugin status + echo "" + echo "--- Plugins ---" + PLUGIN_UPDATES=$(wp plugin list --path="$WP_PATH" --update=available --format=count 2>/dev/null || echo "0") + if [ "$PLUGIN_UPDATES" = "0" ]; then + check_pass "All plugins up to date" + else + check_warn "$PLUGIN_UPDATES plugin(s) need updates" + fi + + INACTIVE_PLUGINS=$(wp plugin list --path="$WP_PATH" --status=inactive --format=count 2>/dev/null || echo "0") + if [ "$INACTIVE_PLUGINS" != "0" ]; then + check_warn "$INACTIVE_PLUGINS inactive plugin(s) — consider removing" + fi +else + check_warn "WP-CLI not available or WP path not found — skipping core checks" +fi + +echo "" + +# 3. Database connectivity +echo "--- Database ---" +if command -v wp &> /dev/null && [ -d "$WP_PATH" ]; then + if wp db check --path="$WP_PATH" &>/dev/null; then + check_pass "Database connection OK" + else + check_fail "Database connection failed" + fi +else + check_warn "Skipping DB check (WP-CLI not available)" +fi + +echo "" + +# 4. Disk space +echo "--- Server ---" +DISK_USAGE=$(df -h / | awk 'NR==2 {print $5}' | sed 's/%//') +if [ "$DISK_USAGE" -lt 80 ]; then + check_pass "Disk usage: ${DISK_USAGE}%" +elif [ "$DISK_USAGE" -lt 90 ]; then + check_warn "Disk usage: ${DISK_USAGE}% — getting full" +else + check_fail "Disk usage: ${DISK_USAGE}% — critical!" +fi + +# PHP version +PHP_VER=$(php -v 2>/dev/null | head -1 | awk '{print $2}' || echo "unknown") +if [ "$PHP_VER" != "unknown" ]; then + check_pass "PHP version: $PHP_VER" +fi + +# Memory +MEM_AVAIL=$(free -m | awk '/Mem:/ {printf "%.0f", $7/$2*100}') +if [ "$MEM_AVAIL" -gt 20 ]; then + check_pass "Available memory: ${MEM_AVAIL}%" +else + check_warn "Available memory: ${MEM_AVAIL}% — low" +fi + +echo "" +echo "=========================================" +echo " Results: ${GREEN}${PASS} passed${NC}, ${YELLOW}${WARN} warnings${NC}, ${RED}${FAIL} failed${NC}" +echo "=========================================" + +# Exit with error if any failures +[ "$FAIL" -eq 0 ] && exit 0 || exit 1 diff --git a/scripts/wp-migrate-strains.sh b/scripts/wp-migrate-strains.sh new file mode 100755 index 0000000..eab5c52 --- /dev/null +++ b/scripts/wp-migrate-strains.sh @@ -0,0 +1,99 @@ +#!/bin/bash +# Strain Data Migration Tool for Weedops +# Imports strain data from CSV into WordPress custom post types +# Usage: ./wp-migrate-strains.sh [wp-path] + +set -euo pipefail + +CSV_FILE="${1:-}" +WP_PATH="${2:-/var/www/weedops}" + +if [ -z "$CSV_FILE" ]; then + echo "Usage: $0 [wp-path]" + echo "" + echo "CSV format: name,type,thc_min,thc_max,cbd_min,cbd_max,effects,description" + echo "Types: indica, sativa, hybrid" + echo "" + echo "Example:" + echo " Blue Dream,hybrid,17,24,0.1,0.2,\"relaxed,happy,creative\",\"Popular hybrid strain\"" + exit 1 +fi + +if [ ! -f "$CSV_FILE" ]; then + echo "ERROR: File not found: $CSV_FILE" + exit 1 +fi + +if ! command -v wp &> /dev/null; then + echo "ERROR: WP-CLI required. Install from https://wp-cli.org/" + exit 1 +fi + +echo "=========================================" +echo " Weedops Strain Data Importer" +echo " $(date '+%Y-%m-%d %H:%M:%S')" +echo "=========================================" +echo "Source: $CSV_FILE" +echo "Target: $WP_PATH" +echo "" + +IMPORTED=0 +SKIPPED=0 +ERRORS=0 + +# Skip header line +tail -n +2 "$CSV_FILE" | while IFS=',' read -r name type thc_min thc_max cbd_min cbd_max effects description; do + # Clean up fields + name=$(echo "$name" | sed 's/^"//;s/"$//' | xargs) + type=$(echo "$type" | sed 's/^"//;s/"$//' | xargs) + description=$(echo "$description" | sed 's/^"//;s/"$//') + + if [ -z "$name" ]; then + continue + fi + + echo -n "Importing: $name... " + + # Check if strain already exists + EXISTING=$(wp post list --path="$WP_PATH" --post_type=strain --name="$(echo "$name" | tr '[:upper:]' '[:lower:]' | tr ' ' '-')" --format=count 2>/dev/null || echo "0") + + if [ "$EXISTING" != "0" ]; then + echo "SKIPPED (already exists)" + ((SKIPPED++)) + continue + fi + + # Create the strain post + POST_ID=$(wp post create \ + --path="$WP_PATH" \ + --post_type=strain \ + --post_title="$name" \ + --post_content="$description" \ + --post_status=publish \ + --porcelain 2>/dev/null || echo "0") + + if [ "$POST_ID" = "0" ]; then + echo "ERROR" + ((ERRORS++)) + continue + fi + + # Set strain type taxonomy + wp term set "$POST_ID" strain_type "$type" --path="$WP_PATH" 2>/dev/null || true + + # Set meta fields + wp post meta update "$POST_ID" thc_min "$thc_min" --path="$WP_PATH" 2>/dev/null || true + wp post meta update "$POST_ID" thc_max "$thc_max" --path="$WP_PATH" 2>/dev/null || true + wp post meta update "$POST_ID" cbd_min "$cbd_min" --path="$WP_PATH" 2>/dev/null || true + wp post meta update "$POST_ID" cbd_max "$cbd_max" --path="$WP_PATH" 2>/dev/null || true + wp post meta update "$POST_ID" effects "$effects" --path="$WP_PATH" 2>/dev/null || true + + echo "OK (ID: $POST_ID)" + ((IMPORTED++)) +done + +echo "" +echo "=========================================" +echo " Import complete" +echo " Imported: $IMPORTED | Skipped: $SKIPPED | Errors: $ERRORS" +echo "========================================="