diff --git a/.ddev/config.yaml b/.ddev/config.yaml index 88209b1b..4e1b8a86 100644 --- a/.ddev/config.yaml +++ b/.ddev/config.yaml @@ -1,27 +1,80 @@ -name: DrupalPod -type: drupal -docroot: web +type: drupal11 +docroot: docroot/web php_version: "8.3" +composer_root: docroot webserver_type: apache-fpm xdebug_enabled: false additional_hostnames: [] additional_fqdns: [] database: - type: mariadb - version: "10.11" + type: mysql + version: "8.0" +hooks: + post-start: + # Set up debugging. + - exec: test -f .vscode/launch.json || mkdir -p .vscode && cp .ddev/launch.json .vscode/ + # Run initial setup tasks only when needed (DP_REBUILD=1 or Drupal not installed). + - exec: 'if [ "${DP_REBUILD:-0}" = "1" ] || ! drush status --field=bootstrap 2>/dev/null | grep -q "Successful"; then bash "$(pwd)/.devpanel/init.sh"; fi' use_dns_when_possible: true composer_version: "2" web_environment: + # Set up DevPanel variables. DO NOT CHANGE - APP_ROOT=$DDEV_COMPOSER_ROOT - - WEB_ROOT=$DDEV_COMPOSER_ROOT/$DDEV_DOCROOT + - WEB_ROOT=$DDEV_APPROOT/$DDEV_DOCROOT - DB_HOST=db - DB_PORT=3306 - DB_USER=db - DB_PASSWORD=db - DB_NAME=db - - DP_APP_ID=drupalpod + - DP_APP_ID=drupalpod_ai_qa - DB_DRIVER=mysql + - DP_AI_MODULES=ai_provider_litellm,ai_search,ai_agents + + - # ============================================================================ + - # DevPanel / Drupal defaults - customisable + - # ============================================================================ + + - # --------------------------------------------------------------------------- + - # Direct mappings to your DDEV environment variables (keep names identical) + - # --------------------------------------------------------------------------- + - DP_STARTER_TEMPLATE=core # Starter template (e.g., core, demo, etc.) + - DP_VERSION=11.x # Drupal version + + - # --------------------------------------------------------------------------- + - # AI module / PR testing defaults + - # - DP_AI_MODULE_VERSION behavior: + - # * Empty = Auto-detect from test module (recommended) + - # * Set explicitly = Validate compatibility (fails if test module incompatible) + - # - DP_AI_ISSUE_FORK / DP_AI_ISSUE_BRANCH: test a PR for the base module + - # --------------------------------------------------------------------------- + - DP_AI_MODULE=ai # Base AI module name + - DP_AI_MODULE_VERSION= # Empty = auto-detect (or set explicitly to validate) + - DP_AI_ISSUE_FORK= + - DP_AI_ISSUE_BRANCH= + + - # --------------------------------------------------------------------------- + - # Test any AI sub-module (ai_search, ai_agents, providers, etc.) + - # - Setting DP_TEST_MODULE will auto-detect required AI version! + - # - The script clones test module first, reads its AI requirement, then clones AI + - # - Use DP_TEST_MODULE_VERSION to pin a release-style version (optional) + - # - Use DP_TEST_MODULE_ISSUE_FORK / DP_TEST_MODULE_ISSUE_BRANCH to test PRs + - # --------------------------------------------------------------------------- + - DP_TEST_MODULE= # Empty = use auto-detected AI version + - DP_TEST_MODULE_VERSION= # Empty = use dev branch + - DP_TEST_MODULE_ISSUE_FORK= + - DP_TEST_MODULE_ISSUE_BRANCH= + + - # --------------------------------------------------------------------------- + - # Optional: local or cloud API key for LiteLLM / provider testing (empty by default) + - # --------------------------------------------------------------------------- + - DP_AI_VIRTUAL_KEY= + + - # --------------------------------------------------------------------------- + - # Set to 1 if you wish to rebuild the whole project from scratch + - # --------------------------------------------------------------------------- + - DP_REBUILD=1 corepack_enable: false +ddev_version_constraint: '>=1.24.0' # Key features of DDEV's config.yaml: diff --git a/.ddev/launch.json b/.ddev/launch.json new file mode 100644 index 00000000..923c99bf --- /dev/null +++ b/.ddev/launch.json @@ -0,0 +1,52 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + + { + "name": "Listen for Xdebug", + "type": "php", + "request": "launch", + "port": 9003, + "pathMappings": { + "/var/www/html": "${workspaceFolder}" + } + }, + { + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 0, + "runtimeArgs": [ + "-dxdebug.start_with_request=yes" + ], + "env": { + "XDEBUG_MODE": "debug,develop", + "XDEBUG_CONFIG": "client_port=${port}" + } + }, + { + "name": "Launch Built-in web server", + "type": "php", + "request": "launch", + "runtimeArgs": [ + "-dxdebug.mode=debug", + "-dxdebug.start_with_request=yes", + "-S", + "localhost:0" + ], + "program": "", + "cwd": "${workspaceRoot}", + "port": 9003, + "serverReadyAction": { + "pattern": "Development Server \\(http://localhost:([0-9]+)\\) started", + "uriFormat": "http://localhost:%s", + "action": "openExternally" + } + } + ] +} diff --git a/.devpanel/cleanup.sh b/.devpanel/cleanup.sh deleted file mode 100755 index 3ffcd95e..00000000 --- a/.devpanel/cleanup.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Remove composer.json. A fresh one will be generated. -rm -f "${APP_ROOT}"/composer.json diff --git a/.devpanel/clone_ai_modules.sh b/.devpanel/clone_ai_modules.sh new file mode 100755 index 00000000..31dcf7e7 --- /dev/null +++ b/.devpanel/clone_ai_modules.sh @@ -0,0 +1,433 @@ +#!/usr/bin/env bash +set -eu -o pipefail + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Directory Setup (works in both DDEV and GitHub Actions environments) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Find the .devpanel directory (where this script lives) +DEVPANEL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Project root is one level up from .devpanel +PROJECT_ROOT="$(dirname "$DEVPANEL_DIR")" + +cd "$PROJECT_ROOT" + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Clone AI Modules from Git (Dependency-Driven Architecture) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Automatically clones AI base module + dependencies from git.drupalcode.org +# Supports version branches, tags, and PR testing +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +# Track which modules we clone and which are compatible +export CLONED_AI_MODULES="" +export COMPATIBLE_AI_MODULES="" # Only modules compatible with AI version + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Helper: Clone a module from git +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +clone_module() { + local module_name=$1 + local module_version=${2:-} + local issue_fork=${3:-} + local issue_branch=${4:-} + + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Cloning: $module_name" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + if git submodule status repos/"$module_name" > /dev/null 2>&1; then + echo " ✓ Submodule exists, updating..." + time git submodule update --init --recursive repos/"$module_name" + else + echo " + Adding as submodule..." + time git submodule add -f https://git.drupalcode.org/project/"$module_name".git repos/"$module_name" + time git config -f .gitmodules submodule."repos/$module_name".ignore dirty + fi + + cd "${APP_ROOT}"/repos/"$module_name" + git fetch origin + git fetch --all --tags + + # Checkout specific PR/issue branch if specified + if [ -n "$issue_branch" ] && [ -n "$issue_fork" ]; then + echo " → Checking out PR: $issue_fork/$issue_branch" + if git show-ref -q --heads "$issue_branch"; then + git checkout "$issue_branch" + else + git remote add issue-"$issue_fork" https://git.drupalcode.org/issue/"$issue_fork".git 2>/dev/null || true + git fetch issue-"$issue_fork" + git checkout -b "$issue_branch" --track issue-"$issue_fork"/"$issue_branch" + fi + elif [ -n "$module_version" ]; then + echo " → Checking out version: $module_version" + # Check if it's a branch (*.x) + if [[ "$module_version" == *.x ]]; then + if git show-ref --verify --quiet refs/remotes/origin/"$module_version"; then + git checkout -B "$module_version" origin/"$module_version" + else + echo " ⚠️ Branch $module_version not found, using latest stable" + latest_tag=$(git tag --sort=-version:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) + if [ -n "$latest_tag" ]; then + git checkout tags/"$latest_tag" + fi + fi + else + # Try as tag, then branch + if git rev-parse tags/"$module_version" >/dev/null 2>&1; then + git checkout tags/"$module_version" + elif git show-ref --verify --quiet refs/remotes/origin/"$module_version"; then + git checkout -B "$module_version" origin/"$module_version" + else + echo " ⚠️ Version $module_version not found, using latest stable" + latest_tag=$(git tag --sort=-version:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) + if [ -n "$latest_tag" ]; then + git checkout tags/"$latest_tag" + fi + fi + fi + else + # No version specified - checkout latest stable tag (by semantic version, not date) + echo " → No version specified, checking out latest stable release" + # Sort tags by semantic version (descending) and pick the highest + latest_tag=$(git tag --sort=-version:refname | grep -E '^[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) + if [ -n "$latest_tag" ]; then + echo " → Found latest stable: $latest_tag" + git checkout tags/"$latest_tag" + else + echo " ⚠️ No tags found, using default branch" + fi + fi + + cd "${APP_ROOT}" + + # Add to cloned modules list + if [ -z "$CLONED_AI_MODULES" ]; then + export CLONED_AI_MODULES="$module_name" + else + export CLONED_AI_MODULES="$CLONED_AI_MODULES,$module_name" + fi +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Helper: Get AI ecosystem dependencies from composer.json +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +get_ai_dependencies() { + local module_path=$1 + local composer_json="${module_path}/composer.json" + + if [ ! -f "$composer_json" ]; then + return + fi + + # Extract drupal/ai* dependencies from require section + # Use grep and sed to parse JSON (simple approach) + grep -A 100 '"require"' "$composer_json" | \ + grep '"drupal/ai' | \ + sed 's/.*"drupal\/\([^"]*\)".*/\1/' || true +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Helper: Extract compatible version from constraint +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +get_compatible_version() { + local module_path=$1 + local dependency_name=$2 + local composer_json="${module_path}/composer.json" + + if [ ! -f "$composer_json" ]; then + echo "" + return + fi + + # Extract version constraint for the dependency + # e.g., "drupal/ai_provider_litellm": "^1.2" → return "1.2.x" + local constraint=$(grep -A 100 '"require"' "$composer_json" | \ + grep "\"drupal/$dependency_name\"" | \ + sed 's/.*"drupal\/[^"]*": *"\([^"]*\)".*/\1/' | \ + head -1 || true) + + if [ -z "$constraint" ]; then + echo "" + return + fi + + # Convert constraint to branch version + # ^1.2 → 1.2.x + # ^1.2.0 → 1.2.x + # ~1.2.0 → 1.2.x + local version=$(echo "$constraint" | sed -E 's/[\^~>=<]//g' | sed -E 's/^([0-9]+\.[0-9]+).*/\1.x/') + echo "$version" +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Helper: Check if module is compatible with AI version +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +is_compatible_with_ai() { + local module_path=$1 + local ai_version=$2 + + # If AI version is empty (using git default branch), we can't check compatibility + # Assume compatible - the actual version will be determined by git + if [ -z "$ai_version" ]; then + return 0 # Can't check compatibility without knowing AI version + fi + + local composer_json="${module_path}/composer.json" + + if [ ! -f "$composer_json" ]; then + return 0 # If no composer.json, assume compatible + fi + + # Get the AI requirement constraint (e.g., "^2.0", "^1.2") + local ai_constraint=$(grep -A 100 '"require"' "$composer_json" | \ + grep '"drupal/ai"' | \ + sed 's/.*"drupal\/ai": *"\([^"]*\)".*/\1/' | \ + head -1 || true) + + if [ -z "$ai_constraint" ]; then + return 0 # If no AI requirement, assume compatible + fi + + # Extract major.minor from constraint (e.g., ^2.0 → 2.0, ^1.2 → 1.2) + local required_version=$(echo "$ai_constraint" | sed -E 's/[\^~>=<]//g' | sed -E 's/^([0-9]+\.[0-9]+).*/\1/') + + # Extract major.minor from our AI version (e.g., 1.2.x → 1.2, 2.0.x → 2.0) + local our_version=$(echo "$ai_version" | sed -E 's/^([0-9]+\.[0-9]+).*/\1/') + + # Simple version comparison: they must match + if [ "$required_version" = "$our_version" ]; then + return 0 # Compatible + else + echo " ⚠️ Incompatible: requires AI ^$required_version, but we have AI $ai_version" >&2 + return 1 # Incompatible + fi +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Main Logic: Hybrid Architecture (Dependency-Driven) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# 1. If DP_TEST_MODULE set: Clone test module first, read AI requirement from composer.json +# 2. If DP_AI_MODULE_VERSION set: Use that specific version (dev branch or tag) +# 3. Otherwise: Use latest stable tag (e.g., 1.2.1, not 1.2.x-dev) +# 4. Clone additional modules from DP_AI_MODULES with compatibility filtering +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +# Determine final AI version to use (empty = latest stable tag) +FINAL_AI_VERSION="${DP_AI_MODULE_VERSION}" + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Step 1: Clone test module first (if specified) to determine AI requirements +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +if [ -n "${DP_TEST_MODULE:-}" ]; then + echo + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step 1: Clone Test Module (determines AI version requirement)" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + + test_version="$DP_TEST_MODULE_VERSION" + clone_module "$DP_TEST_MODULE" "$test_version" "$DP_TEST_MODULE_ISSUE_FORK" "$DP_TEST_MODULE_ISSUE_BRANCH" + + # Get AI requirement from test module + test_module_ai_requirement=$(get_compatible_version "repos/$DP_TEST_MODULE" "$DP_AI_MODULE") + + if [ -n "$test_module_ai_requirement" ]; then + # Check if user explicitly set AI version + if [ "${DP_AI_MODULE_VERSION_EXPLICIT:-no}" = "yes" ]; then + # User explicitly set AI version - validate compatibility + if [ "$test_module_ai_requirement" != "$FINAL_AI_VERSION" ]; then + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "❌ ERROR: Version Conflict" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "" + echo "Test module '$DP_TEST_MODULE' requires AI $test_module_ai_requirement" + echo "But you explicitly configured DP_AI_MODULE_VERSION=$FINAL_AI_VERSION" + echo "" + echo "Fix options:" + echo " 1. Remove DP_AI_MODULE_VERSION from config (auto-detect from test module)" + echo " 2. Change DP_AI_MODULE_VERSION to $test_module_ai_requirement" + echo " 3. Test a different module compatible with AI $FINAL_AI_VERSION" + echo "" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + exit 1 + else + echo " ✓ Test module requires AI $test_module_ai_requirement (matches your configuration)" + fi + else + # User didn't set AI version - auto-detect from test module + FINAL_AI_VERSION="$test_module_ai_requirement" + echo " → Test module requires AI $FINAL_AI_VERSION (auto-detected)" + fi + else + # Couldn't determine requirement from test module + if [ "${DP_AI_MODULE_VERSION_EXPLICIT:-no}" = "no" ]; then + echo " ⚠️ Could not determine AI version from test module's composer.json" + if [ -z "$FINAL_AI_VERSION" ]; then + echo " → Using git repo's default branch (future-proof)" + else + echo " → Using default: $FINAL_AI_VERSION" + fi + else + echo " ⚠️ Could not determine AI version from test module's composer.json" + echo " → Using your configured version: $FINAL_AI_VERSION" + fi + fi + + # Mark test module as compatible (it drove the AI version choice) + if [ -z "$COMPATIBLE_AI_MODULES" ]; then + export COMPATIBLE_AI_MODULES="$DP_TEST_MODULE" + else + export COMPATIBLE_AI_MODULES="$COMPATIBLE_AI_MODULES,$DP_TEST_MODULE" + fi +else + echo + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "Step 1: No Test Module" + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + if [ -z "$FINAL_AI_VERSION" ]; then + echo " → Will use latest stable release tag" + else + echo " → Using explicitly configured AI version: $FINAL_AI_VERSION" + fi +fi + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Step 2: Clone AI base module at determined version +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +echo +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +if [ -z "$FINAL_AI_VERSION" ]; then + echo "Step 2: Clone AI Base Module @ latest stable" +else + echo "Step 2: Clone AI Base Module @ $FINAL_AI_VERSION" +fi +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +clone_module "$DP_AI_MODULE" "$FINAL_AI_VERSION" "$DP_AI_ISSUE_FORK" "$DP_AI_ISSUE_BRANCH" + +# If AI version was empty (latest stable tag), detect the actual version for compatibility filtering +if [ -z "$FINAL_AI_VERSION" ]; then + cd "${APP_ROOT}"/repos/"$DP_AI_MODULE" + + # Try multiple methods to detect version + # Method 1: Current branch name (works if checked out to a branch) + actual_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || true) + + if [[ "$actual_branch" =~ ^[0-9]+\.[0-9x]+$ ]] || [[ "$actual_branch" =~ ^[0-9]+\.x$ ]]; then + FINAL_AI_VERSION="$actual_branch" + echo " → Detected AI version from branch: $FINAL_AI_VERSION" + else + # Method 2: Check which remote branch this commit belongs to (handles detached HEAD) + remote_branch=$(git branch -r --contains HEAD 2>/dev/null | grep -E 'origin/[0-9]+\.(x|[0-9]+\.x)$' | head -1 | xargs | sed 's|.*origin/||' || true) + + if [ -n "$remote_branch" ] && [[ "$remote_branch" =~ ^[0-9]+\.[0-9x]+$ ]]; then + FINAL_AI_VERSION="$remote_branch" + echo " → Detected AI version from remote branch: $FINAL_AI_VERSION" + else + # Method 3: Try git describe to find nearest tag + nearest_tag=$(git describe --tags --abbrev=0 2>/dev/null || true) + if [ -n "$nearest_tag" ]; then + # Convert tag to branch format (e.g., 1.2.0 → 1.2.x, 2.0.0-alpha1 → 2.0.x) + FINAL_AI_VERSION=$(echo "$nearest_tag" | sed -E 's/^([0-9]+\.[0-9]+).*/\1.x/') + echo " → Detected AI version from tag: $FINAL_AI_VERSION (from $nearest_tag)" + fi + fi + fi + + cd "${APP_ROOT}" + + if [ -n "$FINAL_AI_VERSION" ]; then + echo " ✓ Using detected version for compatibility filtering: $FINAL_AI_VERSION" + else + echo " ⚠️ Could not detect AI version - skipping compatibility filtering" + fi +fi + +# AI base is always compatible +if [ -z "$COMPATIBLE_AI_MODULES" ]; then + export COMPATIBLE_AI_MODULES="$DP_AI_MODULE" +else + export COMPATIBLE_AI_MODULES="$COMPATIBLE_AI_MODULES,$DP_AI_MODULE" +fi + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Step 3: Clone additional AI modules (with compatibility filtering) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +echo +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "Step 3: Clone Additional AI Modules (compatibility filtering)" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +# Parse DP_AI_MODULES (comma-separated list) +if [ -n "${DP_AI_MODULES:-}" ]; then + IFS=',' read -ra MODULES <<< "$DP_AI_MODULES" + for module in "${MODULES[@]}"; do + module=$(echo "$module" | xargs) # Trim whitespace + + if [ -z "$module" ]; then + continue + fi + + # Skip if already cloned (e.g., as test module) + if echo ",$CLONED_AI_MODULES," | grep -q ",$module,"; then + echo " ✓ $module already cloned" + continue + fi + + # Clone at dev branch + echo " → Cloning $module..." + clone_module "$module" "" + + # Check compatibility with AI version + if is_compatible_with_ai "repos/$module" "$FINAL_AI_VERSION"; then + echo " ✓ $module is compatible with AI $FINAL_AI_VERSION" + if [ -z "$COMPATIBLE_AI_MODULES" ]; then + export COMPATIBLE_AI_MODULES="$module" + else + export COMPATIBLE_AI_MODULES="$COMPATIBLE_AI_MODULES,$module" + fi + else + echo " ✗ Skipping $module from composer (incompatible, but cloned for inspection)" + fi + done +else + echo " (No additional modules specified in DP_AI_MODULES)" +fi + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Step 4: Clone default provider +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +echo +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "Step 4: Clone Default Provider" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +# Always clone ai_provider_litellm unless already cloned +if echo ",$CLONED_AI_MODULES," | grep -q ",ai_provider_litellm,"; then + echo " ✓ Provider already cloned" +else + provider_version=$(get_compatible_version "repos/$DP_AI_MODULE" "ai_provider_litellm") + if [ -z "$provider_version" ]; then + provider_version="$FINAL_AI_VERSION" + fi + echo " → Cloning default provider: ai_provider_litellm @ $provider_version" + clone_module "ai_provider_litellm" "$provider_version" + + # Check compatibility + if is_compatible_with_ai "repos/ai_provider_litellm" "$FINAL_AI_VERSION"; then + echo " ✓ Provider is compatible" + export COMPATIBLE_AI_MODULES="$COMPATIBLE_AI_MODULES,ai_provider_litellm" + else + echo " ✗ Provider incompatible (skipping from composer)" + fi +fi + +echo +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo "✓ Summary" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo " AI Version: $FINAL_AI_VERSION" +echo " Cloned modules: $CLONED_AI_MODULES" +echo " Compatible (will be added to composer): $COMPATIBLE_AI_MODULES" +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" +echo diff --git a/.devpanel/composer_setup.sh b/.devpanel/composer_setup.sh index 10fca381..b82b13e4 100755 --- a/.devpanel/composer_setup.sh +++ b/.devpanel/composer_setup.sh @@ -1,27 +1,76 @@ #!/usr/bin/env bash set -eu -o pipefail -cd $APP_ROOT - -# For versions end with x - add `-dev` suffix (ie. 9.3.x-dev) -# For versions without x - add `~` prefix (ie. ~9.2.0) -d="$DP_CORE_VERSION" -case $d in -*.x) - install_version="$d"-dev - ;; -*) - install_version=~"$d" - ;; -esac - -if [ ! -f composer.json ]; then - # Create required composer.json and composer.lock files - time composer create-project -n --no-install drupal/recommended-project:"$install_version" temp-composer-files - cp "$APP_ROOT"/temp-composer-files/* "$APP_ROOT"/. - rm -rf "$APP_ROOT"/temp-composer-files + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Directory Setup (works in both DDEV and GitHub Actions environments) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Find the .devpanel directory (where this script lives) +DEVPANEL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Project root is one level up from .devpanel +PROJECT_ROOT="$(dirname "$DEVPANEL_DIR")" +# APP_ROOT is the composer root (from environment or default to PROJECT_ROOT) +APP_ROOT="${APP_ROOT:-$PROJECT_ROOT}" + +cd "$APP_ROOT" + +# Determine which starter template to use. +# Options: "cms" or "core" +STARTER_TEMPLATE="${DP_STARTER_TEMPLATE:-cms}" + +# Determine the composer package and version. +if [ "$STARTER_TEMPLATE" = "cms" ]; then + COMPOSER_PROJECT="drupal/cms" + # For CMS versions: 1.x, 1.0.0, 2.0.0, etc. + d="$DP_VERSION" + case $d in + *.x) + install_version="$d"-dev + ;; + *) + install_version="$d" + ;; + esac +else + COMPOSER_PROJECT="drupal/recommended-project" + # For core versions: 11.x, 11.2.8, etc. + d="$DP_VERSION" + case $d in + *.x) + install_version="$d"-dev + ;; + *) + install_version=~"$d" + ;; + esac fi -# Programmatically fix Composer 2.2 allow-plugins to avoid errors +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Generate composer.json from CMS/Core template using temp directory +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Why do we copy it to temp? +# - `composer create-project` requires an empty/non-existent target directory +# - Our project root may have existing files (git, config, etc.) +# - Solution: Create in temp, copy everything over, then customize composer.json +# - This is safer than deleting project root first (preserves files if script fails) +# +# Clean up temp directory if it exists from previous runs. +rm -rf temp-composer-files + +# Create the project template in temp directory. +if [ -n "$install_version" ]; then + time composer create-project -n --no-install "$COMPOSER_PROJECT":"$install_version" temp-composer-files +else + time composer create-project -n --no-install "$COMPOSER_PROJECT" temp-composer-files +fi + +# Copy all files (including hidden files) from temp to docroot. +cp -r temp-composer-files/. . + +# Clean up temp directory. +rm -rf temp-composer-files + +# Programmatically fix Composer 2.2 allow-plugins to avoid errors. +# IMPORTANT: Do this FIRST before any other composer config commands to avoid warnings. composer config --no-plugins allow-plugins.composer/installers true composer config --no-plugins allow-plugins.drupal/core-project-message true composer config --no-plugins allow-plugins.drupal/core-vendor-hardening true @@ -31,23 +80,287 @@ composer config --no-plugins allow-plugins.phpstan/extension-installer true composer config --no-plugins allow-plugins.mglaman/composer-drupal-lenient true composer config --no-plugins allow-plugins.php-http/discovery true composer config --no-plugins allow-plugins.tbachert/spi false +composer config --no-plugins allow-plugins.cweagans/composer-patches true -# Add project source code as symlink (to repos/name_of_project) -# double quotes explained - https://stackoverflow.com/a/1250279/5754049 -if [ -n "$DP_PROJECT_NAME" ]; then - composer --no-plugins config \ - repositories.core1 \ - '{"type": "path", "url": "repos/'"$DP_PROJECT_NAME"'", "options": {"symlink": true}}' +# Set minimum-stability to dev to allow alpha/beta packages (needed for dev versions). +composer config minimum-stability dev - composer --no-plugins config minimum-stability dev -fi +# Allow patches to fail without stopping installation. +composer config extra.composer-exit-on-patch-failure false # Scaffold settings.php. -composer config -jm extra.drupal-scaffold.file-mapping '{ - "[web-root]/sites/default/settings.php": { - "path": "web/core/assets/scaffold/files/default.settings.php", - "overwrite": false - } -}' +composer config --json extra.drupal-scaffold.file-mapping '{"[web-root]/sites/default/settings.php":{"path":"web/core/assets/scaffold/files/default.settings.php","overwrite":false}}' composer config scripts.post-drupal-scaffold-cmd \ - 'cd web/sites/default && test -z "$(grep '\''include \$devpanel_settings;'\'' settings.php)" && patch -Np1 -r /dev/null < $APP_ROOT/.devpanel/drupal-settings.patch || :' + 'cd web/sites/default && test -z "$(grep '\''include \$devpanel_settings;'\'' settings.php)" && patch -Np1 -r /dev/null < $DIR/drupal-settings.patch || :' + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# AI MODULES FROM GIT (Path Repositories) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Add path repositories ONLY for compatible AI modules +# Incompatible modules are cloned (available in repos/) but not added to composer +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +if [ -n "${COMPATIBLE_AI_MODULES:-}" ]; then + echo "Adding path repositories for compatible AI modules..." + + IFS=',' read -ra MODULES <<< "$COMPATIBLE_AI_MODULES" + for module in "${MODULES[@]}"; do + module=$(echo "$module" | xargs) # Trim whitespace + + if [ -z "$module" ]; then + continue + fi + + if [ -d "$APP_ROOT/repos/$module" ]; then + echo " - Adding path repository for: $module" + composer config --no-plugins repositories."$module"-git \ + "{\"type\": \"path\", \"url\": \"$APP_ROOT/repos/$module\", \"options\": {\"symlink\": true}}" + + # Require from path (use *@dev to accept version from module's composer.json). + composer require -n --no-update "drupal/$module:*@dev" + fi + done + + echo "Path repositories added for compatible AI modules!" +fi + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# AI DEPENDENCIES +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +if [ "$STARTER_TEMPLATE" = "cms" ]; then + echo "Adding CMS dependencies (full setup with webform libraries)..." + + # Add JavaScript library repositories for Webform support. + composer config repositories.tippyjs '{ + "type": "package", + "package": { + "name": "tippyjs/tippyjs", + "version": "6.3.7", + "type": "drupal-library", + "extra": { + "installer-name": "tippyjs" + }, + "dist": { + "url": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "type": "tar" + }, + "license": "MIT" + } + }' + + composer config repositories.tabby '{ + "type": "package", + "package": { + "name": "tabby/tabby", + "version": "12.0.3", + "type": "drupal-library", + "extra": { + "installer-name": "tabby" + }, + "dist": { + "url": "https://github.com/cferdinandi/tabby/archive/refs/tags/v12.0.3.zip", + "type": "zip" + }, + "license": "MIT" + } + }' + + composer config repositories.signature_pad '{ + "type": "package", + "package": { + "name": "signature_pad/signature_pad", + "version": "2.3.0", + "type": "drupal-library", + "extra": { + "installer-name": "signature_pad" + }, + "dist": { + "url": "https://github.com/szimek/signature_pad/archive/refs/tags/v2.3.0.zip", + "type": "zip" + }, + "license": "MIT" + } + }' + + composer config repositories.progress-tracker '{ + "type": "package", + "package": { + "name": "progress-tracker/progress-tracker", + "version": "2.0.7", + "type": "drupal-library", + "extra": { + "installer-name": "progress-tracker" + }, + "dist": { + "url": "https://github.com/NigelOToole/progress-tracker/archive/refs/tags/2.0.7.zip", + "type": "zip" + }, + "license": "MIT" + } + }' + + composer config repositories.popperjs '{ + "type": "package", + "package": { + "name": "popperjs/popperjs", + "version": "2.11.6", + "type": "drupal-library", + "extra": { + "installer-name": "popperjs" + }, + "dist": { + "url": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "type": "tar" + }, + "license": "MIT" + } + }' + + composer config repositories."jquery.timepicker" '{ + "type": "package", + "package": { + "name": "jquery/timepicker", + "version": "1.14.0", + "type": "drupal-library", + "extra": { + "installer-name": "jquery.timepicker" + }, + "dist": { + "url": "https://github.com/jonthornton/jquery-timepicker/archive/refs/tags/1.14.0.zip", + "type": "zip" + }, + "license": "MIT" + } + }' + + composer config repositories."jquery.textcounter" '{ + "type": "package", + "package": { + "name": "jquery/textcounter", + "version": "0.9.1", + "type": "drupal-library", + "extra": { + "installer-name": "jquery.textcounter" + }, + "dist": { + "url": "https://github.com/ractoon/jQuery-Text-Counter/archive/refs/tags/0.9.1.zip", + "type": "zip" + }, + "license": "MIT" + } + }' + + composer config repositories."jquery.select2" '{ + "type": "package", + "package": { + "name": "jquery/select2", + "version": "4.0.13", + "type": "drupal-library", + "extra": { + "installer-name": "jquery.select2" + }, + "dist": { + "url": "https://github.com/select2/select2/archive/refs/tags/4.0.13.zip", + "type": "zip" + }, + "license": "MIT" + } + }' + + composer config repositories."jquery.rateit" '{ + "type": "package", + "package": { + "name": "jquery/rateit", + "version": "1.1.5", + "type": "drupal-library", + "extra": { + "installer-name": "jquery.rateit" + }, + "dist": { + "url": "https://github.com/gjunge/rateit.js/archive/refs/tags/1.1.5.zip", + "type": "zip" + }, + "license": "MIT" + } + }' + + composer config repositories."jquery.intl-tel-input" '{ + "type": "package", + "package": { + "name": "jquery/intl-tel-input", + "version": "17.0.19", + "type": "drupal-library", + "extra": { + "installer-name": "jquery.intl-tel-input" + }, + "dist": { + "url": "https://github.com/jackocnr/intl-tel-input/archive/refs/tags/v17.0.19.zip", + "type": "zip" + }, + "license": "MIT" + } + }' + + composer config repositories."jquery.inputmask" '{ + "type": "package", + "package": { + "name": "jquery/inputmask", + "version": "5.0.9", + "type": "drupal-library", + "extra": { + "installer-name": "jquery.inputmask" + }, + "dist": { + "url": "https://github.com/RobinHerbots/jquery.inputmask/archive/refs/tags/5.0.9.zip", + "type": "zip" + }, + "license": "MIT" + } + }' + + composer config repositories.codemirror '{ + "type": "package", + "package": { + "name": "codemirror/codemirror", + "version": "5.65.12", + "type": "drupal-library", + "extra": { + "installer-name": "codemirror" + }, + "dist": { + "url": "https://github.com/components/codemirror/archive/refs/tags/5.65.12.zip", + "type": "zip" + }, + "license": "MIT" + } + }' + + # Require all CMS dependencies (Webform libraries only - AI modules come from git). + composer require -n --no-update --dev \ + cweagans/composer-patches:^2@beta \ + drush/drush:^13.6 \ + codemirror/codemirror \ + jquery/inputmask \ + jquery/intl-tel-input \ + jquery/rateit \ + jquery/select2 \ + jquery/textcounter \ + jquery/timepicker \ + popperjs/popperjs \ + progress-tracker/progress-tracker \ + signature_pad/signature_pad \ + tabby/tabby \ + tippyjs/tippyjs + +else + echo "Adding Core AI dependencies (lean setup for quick PR/issue testing)..." + + # Core variant: Search only - AI modules come from git via path repos. + composer require -n --no-update \ + cweagans/composer-patches:^2@beta \ + drush/drush:^13.6 \ + drupal/search_api \ + drupal/search_api_db \ + drupal/token +fi diff --git a/.devpanel/contrib_modules_setup.sh b/.devpanel/contrib_modules_setup.sh deleted file mode 100755 index 6892b60a..00000000 --- a/.devpanel/contrib_modules_setup.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Check if additional modules should be installed -export DEVEL_NAME="devel" -export DEVEL_PACKAGE="drupal/devel" - -export ADMIN_TOOLBAR_NAME="admin_toolbar_tools" -export ADMIN_TOOLBAR_PACKAGE="drupal/admin_toolbar" - -# TODO: once Drupalpod extension supports additional modules - remove these 2 lines -export DP_EXTRA_DEVEL=1 -export DP_EXTRA_ADMIN_TOOLBAR=1 - -# Adding support for composer-drupal-lenient - https://packagist.org/packages/mglaman/composer-drupal-lenient -if [[ "$DP_CORE_VERSION" =~ ^10(\..*)?$ ]]; then - if [ "$DP_PROJECT_TYPE" != "project_core" ]; then - export COMPOSER_DRUPAL_LENIENT=mglaman/composer-drupal-lenient - else - export COMPOSER_DRUPAL_LENIENT='' - fi -fi - -# Adding support for composer-drupal-lenient - https://packagist.org/packages/mglaman/composer-drupal-lenient -if [[ "$DP_CORE_VERSION" =~ ^11(\..*)?$ ]]; then - # admin_toolbar and devel are not compatible yet with Drupal 11 - export DP_EXTRA_ADMIN_TOOLBAR= - export DP_EXTRA_DEVEL= - if [ "$DP_PROJECT_TYPE" != "project_core" ]; then - export COMPOSER_DRUPAL_LENIENT=mglaman/composer-drupal-lenient - else - export COMPOSER_DRUPAL_LENIENT='' - fi -fi diff --git a/.devpanel/create_quickstart.sh b/.devpanel/create_quickstart.sh index cf39db09..31fb9656 100755 --- a/.devpanel/create_quickstart.sh +++ b/.devpanel/create_quickstart.sh @@ -19,6 +19,20 @@ echo -e "-------------------------------" echo -e "| DevPanel Quickstart Creator |" echo -e "-------------------------------\n" +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Directory Setup (works in both DDEV and GitHub Actions environments) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Find the .devpanel directory (where this script lives) +DEVPANEL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Project root is one level up from .devpanel +PROJECT_ROOT="$(dirname "$DEVPANEL_DIR")" +# APP_ROOT is the composer root (from environment or default to PROJECT_ROOT) +APP_ROOT="${APP_ROOT:-$PROJECT_ROOT}" +# WEB_ROOT from environment (with fallback) +WEB_ROOT="${WEB_ROOT:-$APP_ROOT/web}" + +# Define drush command +DRUSH="$APP_ROOT/vendor/bin/drush" # Preparing WORK_DIR=$APP_ROOT @@ -32,8 +46,8 @@ mkdir -p $DUMPS_DIR cd $WORK_DIR echo -e "> Export database to $APP_ROOT/.devpanel/dumps" mkdir -p $APP_ROOT/.devpanel/dumps -drush cr --quiet -drush sql-dump --result-file=../.devpanel/dumps/db.sql --gzip --extra-dump=--no-tablespaces +$DRUSH cr --quiet +$DRUSH sql-dump --result-file=../.devpanel/dumps/db.sql --gzip --extra-dump=--no-tablespaces # Step 2 - Compress static files cd $WORK_DIR diff --git a/.devpanel/custom_package_installer.sh b/.devpanel/custom_package_installer.sh index bc5e167c..1b4506db 100755 --- a/.devpanel/custom_package_installer.sh +++ b/.devpanel/custom_package_installer.sh @@ -38,6 +38,10 @@ if ! php --ri uploadprogress > /dev/null 2>&1; then sudo pecl install uploadprogress echo 'extension=uploadprogress.so' | sudo tee /usr/local/etc/php/conf.d/uploadprogress.ini fi + +# Install the pgvector extension. +source .devpanel/pgvector_installer.sh + # Reload Apache if it's running. if $PECL_UPDATED && sudo /etc/init.d/apache2 status > /dev/null; then sudo /etc/init.d/apache2 reload diff --git a/.devpanel/drupal_setup_contrib.sh b/.devpanel/drupal_setup_contrib.sh deleted file mode 100755 index 4d690f8f..00000000 --- a/.devpanel/drupal_setup_contrib.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail -cd "${APP_ROOT}" - -# Drupal projects with no composer.json, bypass the symlink config, symlink has to be created manually. - -if [ "$DP_PROJECT_TYPE" == "project_module" ]; then - PROJECT_TYPE=modules -elif [ "$DP_PROJECT_TYPE" == "project_theme" ]; then - PROJECT_TYPE=themes -fi - -cat <"${APP_ROOT}"/repos/add-project-as-symlink.sh -#!/usr/bin/env bash -# This file was dynamically generated by a script -echo "Replace project with a symlink" -rm -rf web/$PROJECT_TYPE/contrib/$DP_PROJECT_NAME -cd web/$PROJECT_TYPE/contrib && ln -s ../../../repos/$DP_PROJECT_NAME . -PROJECTASYMLINK - -chmod +x "${APP_ROOT}"/repos/add-project-as-symlink.sh - -if [ -n "$COMPOSER_DRUPAL_LENIENT" ]; then - # Add composer_drupal_lenient for modules on Drupal 10 - composer config --no-plugins --merge --json extra.drupal-lenient.allowed-list '["drupal/'"$DP_PROJECT_NAME"'"]' - time composer require "$COMPOSER_DRUPAL_LENIENT" -fi -# Add the project to composer (it will get the version according to the branch under `/repo/name_of_project`) -time composer require drupal/"$DP_PROJECT_NAME" --no-interaction --no-install diff --git a/.devpanel/drupal_setup_core.sh b/.devpanel/drupal_setup_core.sh deleted file mode 100755 index 5a3957d9..00000000 --- a/.devpanel/drupal_setup_core.sh +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail -cd "${APP_ROOT}" - -# Add a special path when working on core contributions -# (Without it, /web/modules/contrib is not found by website) -composer config --no-plugins repositories.drupal-core2 '{"type": "path", "url": "repos/drupal/core"}' -composer config --no-plugins repositories.drupal-core3 '{"type": "path", "url": "repos/drupal/composer/Metapackage/CoreRecommended"}' -composer config --no-plugins repositories.drupal-core4 '{"type": "path", "url": "repos/drupal/composer/Metapackage/DevDependencies"}' -composer config --no-plugins repositories.drupal-core5 '{"type": "path", "url": "repos/drupal/composer/Plugin/ProjectMessage"}' -composer config --no-plugins repositories.drupal-core6 '{"type": "path", "url": "repos/drupal/composer/Plugin/VendorHardening"}' - -# Removing the conflict part of composer -echo "$(cat composer.json | jq 'del(.conflict)' --indent 4)" >composer.json - -# If a core issue branch was chosen, we want the version of Drupal core that is in that issue branch -# This is very helpful for issues that started with previous Drupal core versions, and the issue version automatically got updated to latest current drupal version -if [ "$DP_PROJECT_TYPE" == "project_core" ] && [ -n "$DP_ISSUE_BRANCH" ]; then - time composer require drupal/core-recommended:* drupal/core-project-message:* drupal/core-composer-scaffold:* --no-update -fi - -# Only after composer update, /web/core get symlinked to /repos/drupal/core -# repos/drupal/core -> web/core -time composer update --no-plugins --no-install - -# vendor -> repos/drupal/vendor -if [ ! -L "$APP_ROOT"/repos/drupal/vendor ]; then - cd "$APP_ROOT"/repos/drupal && - ln -s ../../vendor . -fi - -# Create folders for running tests -mkdir -p "$APP_ROOT"/web/sites/simpletest -mkdir -p "$APP_ROOT"/web/sites/simpletest/browser_output - -# Symlink the simpletest folder into the Drupal core git repo. -# repos/drupal/sites/simpletest -> ../../../web/sites/simpletest -if [ ! -L "$APP_ROOT"/repos/drupal/sites/simpletest ]; then - cd "$APP_ROOT"/repos/drupal/sites && - ln -s ../../../web/sites/simpletest . -fi diff --git a/.devpanel/fallback_setup.sh b/.devpanel/fallback_setup.sh index e591b12a..6c4bf969 100755 --- a/.devpanel/fallback_setup.sh +++ b/.devpanel/fallback_setup.sh @@ -1,10 +1,148 @@ #!/usr/bin/env bash set -eu -o pipefail -# Set a default setup (when project type is not specified) -export DP_INSTALL_PROFILE='demo_umami' -export DP_PROJECT_TYPE='project_core' -export DP_PROJECT_NAME="drupal" -export DP_CORE_VERSION=${DP_CORE_VERSION:='11.2.5'} +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# DevPanel/GitHub Actions: Auto-detect starter template from Docker image tag +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# This ensures image tag is authoritative over env vars to prevent conflicts +# DDEV: DP_IMAGE not set, skips this section and uses defaults below +if [ -n "${DP_IMAGE:-}" ]; then + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo "🐳 DevPanel Mode: Image-based configuration" + echo " Using Docker image: $DP_IMAGE" + + # Extract tag from image (after the colon) + IMAGE_TAG="${DP_IMAGE##*:}" + + # Detect starter template from tag + if [[ "$IMAGE_TAG" == *"core"* ]]; then + DETECTED_TEMPLATE="core" + elif [[ "$IMAGE_TAG" == *"cms"* ]]; then + DETECTED_TEMPLATE="cms" + elif [[ "$IMAGE_TAG" == "latest" ]]; then + DETECTED_TEMPLATE="cms" # latest = cms by convention + else + DETECTED_TEMPLATE="" # Unknown tag, use defaults below + fi + + # Make image tag authoritative + if [ -n "$DETECTED_TEMPLATE" ]; then + if [ -n "${DP_STARTER_TEMPLATE:-}" ] && [ "$DP_STARTER_TEMPLATE" != "$DETECTED_TEMPLATE" ]; then + echo " ⚠️ Conflict detected: Image='$DETECTED_TEMPLATE', Env='$DP_STARTER_TEMPLATE'" + echo " ✓ Using image value (image is authoritative)" + else + echo " ✓ Detected: $DETECTED_TEMPLATE" + fi + export DP_STARTER_TEMPLATE="$DETECTED_TEMPLATE" + fi + echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + echo +fi + +# Set defaults for DrupalPod AI QA (DDEV and fallback for DevPanel) +export DP_STARTER_TEMPLATE=${DP_STARTER_TEMPLATE:='cms'} + +# Set default version based on starter template +if [ "$DP_STARTER_TEMPLATE" = "cms" ]; then + # CMS versions: 1.0.0, 1.1.x, 2.0.0, etc. + export DP_VERSION=${DP_VERSION:='1.x'} +else + # Core versions: 11.2.8, 11.x, 10.1.5, etc. + export DP_VERSION=${DP_VERSION:='11.2.8'} +fi + +# Optional: Enable extra modules (disabled for Drupal 11) export DP_EXTRA_DEVEL=1 export DP_EXTRA_ADMIN_TOOLBAR=1 + +# Default install profile (can be overridden) +if [ -z "${DP_INSTALL_PROFILE:-}" ]; then + if [ "$DP_STARTER_TEMPLATE" = "cms" ]; then + # For CMS, empty string means auto-detect drupal_cms_installer + export DP_INSTALL_PROFILE='' + else + # For core, use standard profile by default + export DP_INSTALL_PROFILE='standard' + fi +fi + +# Validate version format matches template choice +if [ "$DP_STARTER_TEMPLATE" = "cms" ]; then + # CMS versions should be like 1.x, 2.x, 1.0.0, 2.0.0, not like 9.x or higher (which are core) + if [[ "$DP_VERSION" =~ ^[0-9]+ ]]; then + major_version=$(echo "$DP_VERSION" | cut -d. -f1) + if [ "$major_version" -ge 9 ]; then + echo "ERROR: Version '$DP_VERSION' looks like a Drupal core version, but you selected CMS template." + echo "CMS versions should be like: 1.0.0, 1.1.x, 1.x-dev, 2.0.0, 2.x, etc." + exit 1 + fi + fi +else + # Core versions should be like 9.x, 10.x, 11.2.8, not like 1.0.0 or 2.0.0 + if [[ "$DP_VERSION" =~ ^[1-2]\. ]]; then + echo "ERROR: Version '$DP_VERSION' looks like a CMS version, but you selected core template." + echo "Core versions should be like: 11.2.8, 11.x, 10.1.5, 10.x, 9.x" + exit 1 + fi +fi + +# Set install profile based on starter template (only if not already set) +# For CMS, don't specify a profile - let Drupal auto-detect the distribution +if [ -z "${DP_INSTALL_PROFILE+x}" ]; then + if [ "$DP_STARTER_TEMPLATE" = "cms" ]; then + export DP_INSTALL_PROFILE='' + else + export DP_INSTALL_PROFILE='standard' + fi +fi + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# AI MODULE CONFIGURATION (Dependency-Driven Architecture) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# AI version is dynamically determined from: +# 1. Test module's composer.json (if DP_TEST_MODULE set) +# 2. Latest dev branch (if not set) +# No hardcoded version mappings - let dependencies drive the version! +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +# AI base module (ALWAYS cloned from git) +export DP_AI_MODULE=${DP_AI_MODULE:-'ai'} + +# Track if user explicitly set AI version (for validation when testing modules) +if [ -z "${DP_AI_MODULE_VERSION:-}" ]; then + # User didn't set it - leave empty for auto-detection from test module + # If no test module, clone_ai_modules.sh will use latest dev branch + export DP_AI_MODULE_VERSION="" + export DP_AI_MODULE_VERSION_EXPLICIT="no" + echo "📦 AI version will be auto-detected from test module dependencies" +else + # User explicitly set it - validate compatibility when testing modules + export DP_AI_MODULE_VERSION_EXPLICIT="yes" + echo "📦 AI version explicitly set to: $DP_AI_MODULE_VERSION" +fi + +export DP_AI_ISSUE_FORK=${DP_AI_ISSUE_FORK:-''} +export DP_AI_ISSUE_BRANCH=${DP_AI_ISSUE_BRANCH:-''} + +# Generic test module (optional - any module you're testing) +export DP_TEST_MODULE=${DP_TEST_MODULE:-''} # e.g., 'ai_search', 'ai_provider_litellm', 'ai_agents' +export DP_TEST_MODULE_VERSION=${DP_TEST_MODULE_VERSION:-''} # Optional: specific version/branch +export DP_TEST_MODULE_ISSUE_FORK=${DP_TEST_MODULE_ISSUE_FORK:-''} +export DP_TEST_MODULE_ISSUE_BRANCH=${DP_TEST_MODULE_ISSUE_BRANCH:-''} + +# Show final AI module configuration +echo "📦 AI Module Configuration:" +echo " - AI Base: $DP_AI_MODULE @ $DP_AI_MODULE_VERSION" +if [ -n "${DP_AI_ISSUE_BRANCH:-}" ]; then + echo " └─ Testing PR: $DP_AI_ISSUE_FORK/$DP_AI_ISSUE_BRANCH" +fi +if [ -n "${DP_TEST_MODULE:-}" ]; then + echo " - Test Module: $DP_TEST_MODULE" + if [ -n "${DP_TEST_MODULE_VERSION:-}" ]; then + echo " └─ Version: $DP_TEST_MODULE_VERSION" + fi + if [ -n "${DP_TEST_MODULE_ISSUE_BRANCH:-}" ]; then + echo " └─ Testing PR: $DP_TEST_MODULE_ISSUE_FORK/$DP_TEST_MODULE_ISSUE_BRANCH" + fi +fi +echo " - Dependencies: Auto-resolved from composer.json" diff --git a/.devpanel/git_setup.sh b/.devpanel/git_setup.sh deleted file mode 100755 index 1231324b..00000000 --- a/.devpanel/git_setup.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Add git.drupal.org to known_hosts -mkdir -p ~/.ssh -host=git.drupal.org -SSHKey=$(ssh-keyscan $host 2>/dev/null) -echo "$SSHKey" >>~/.ssh/known_hosts - -# Ignore specific directories during Drupal core development -cp "${APP_ROOT}"/.gitpod/drupal/templates/git-exclude.template "${APP_ROOT}"/.git/info/exclude - -# Get the required repo ready -if [ "$DP_PROJECT_TYPE" == "project_core" ]; then - # Find if requested core version is dev or stable - d="$DP_CORE_VERSION" - case $d in - *.x) - # If dev - use git checkout origin/* - checkout_type=origin - ;; - *) - # stable - use git checkout tags/* - checkout_type=tags - ;; - esac - - # Clone Drupal core repo - if git submodule status repos/drupal > /dev/null 2>&1; then - time git submodule update --init --recursive - else - time git submodule add -f https://git.drupalcode.org/project/drupal.git repos/drupal - time git config -f .gitmodules submodule."repos/drupal".ignore dirty - fi - - # Use origin or tags in git checkout command - cd "${APP_ROOT}"/repos/drupal && - git fetch origin && - git fetch --all --tags && - git checkout "$checkout_type"/"$DP_CORE_VERSION" - - # Ignore specific directories during Drupal core development - cp "${APP_ROOT}"/.gitpod/drupal/templates/git-exclude.template "${APP_ROOT}"/.git/modules/repos/drupal/info/exclude -else - # If not core - clone selected project into /repos and remove existing repos. - if git submodule status repos/"$DP_PROJECT_NAME" > /dev/null 2>&1; then - time git submodule update --init --recursive - else - git rm -r repos/*/ - time git submodule add -f https://git.drupalcode.org/project/"$DP_PROJECT_NAME".git repos/"$DP_PROJECT_NAME" - time git config -f .gitmodules submodule."repos/$DP_PROJECT_NAME".ignore dirty - fi -fi - -# Set WORK_DIR -export WORK_DIR="${APP_ROOT}"/repos/$DP_PROJECT_NAME - -# Checkout specific branch only if there's issue_branch -if [ -n "$DP_ISSUE_BRANCH" ]; then - # If branch already exist only run checkout, - if cd "${WORK_DIR}" && git show-ref -q --heads "$DP_ISSUE_BRANCH"; then - cd "${WORK_DIR}" && git checkout "$DP_ISSUE_BRANCH" - else - cd "${WORK_DIR}" && git remote add "$DP_ISSUE_FORK" https://git.drupalcode.org/issue/"$DP_ISSUE_FORK".git - cd "${WORK_DIR}" && git fetch "$DP_ISSUE_FORK" - cd "${WORK_DIR}" && git checkout -b "$DP_ISSUE_BRANCH" --track "$DP_ISSUE_FORK"/"$DP_ISSUE_BRANCH" - fi -elif [ -n "$DP_MODULE_VERSION" ] && [ "$DP_PROJECT_TYPE" != "project_core" ]; then - cd "${WORK_DIR}" && git checkout "$DP_MODULE_VERSION" -fi - -git add . diff --git a/.devpanel/init-container.sh b/.devpanel/init-container.sh index d7fb60fb..26178e4b 100755 --- a/.devpanel/init-container.sh +++ b/.devpanel/init-container.sh @@ -15,23 +15,36 @@ # For GNU Affero General Public License see . # ---------------------------------------------------------------------- +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Directory Setup (works in both DDEV and GitHub Actions environments) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Find the .devpanel directory (where this script lives) +DEVPANEL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Project root is one level up from .devpanel +PROJECT_ROOT="$(dirname "$DEVPANEL_DIR")" +# APP_ROOT is the composer root (from environment or default to PROJECT_ROOT) +APP_ROOT="${APP_ROOT:-$PROJECT_ROOT}" + +# Define drush command +DRUSH="$APP_ROOT/vendor/bin/drush" + #== Import database if [[ $(mysql -h$DB_HOST -P$DB_PORT -u$DB_USER -p$DB_PASSWORD $DB_NAME -e "show tables;") == '' ]]; then if [[ -f "$APP_ROOT/.devpanel/dumps/db.sql.gz" ]]; then echo 'Import mysql file ...' - drush sqlq --file="$APP_ROOT/.devpanel/dumps/db.sql.gz" --file-delete + $DRUSH sqlq --file="$APP_ROOT/.devpanel/dumps/db.sql.gz" --file-delete fi fi if [[ -n "$DB_SYNC_VOL" ]]; then if [[ ! -f "/var/www/build/.devpanel/init-container.sh" ]]; then echo 'Sync volume...' - sudo chown -R 1000:1000 /var/www/build + sudo chown -R 1000:1000 /var/www/build rsync -av --delete --delete-excluded $APP_ROOT/ /var/www/build fi fi -drush -n updb +$DRUSH -n updb echo echo 'Run cron.' -drush cron +$DRUSH cron diff --git a/.devpanel/init.sh b/.devpanel/init.sh index c34262ff..7fcece85 100755 --- a/.devpanel/init.sh +++ b/.devpanel/init.sh @@ -1,125 +1,121 @@ #!/usr/bin/env bash -set -eu -o pipefail - -# Initialize all variables with null if they do not exist -: "${DEBUG_SCRIPT:=}" -: "${DP_INSTALL_PROFILE:=}" -: "${DP_EXTRA_DEVEL:=}" -: "${DP_EXTRA_ADMIN_TOOLBAR:=}" -: "${DP_PROJECT_TYPE:=}" -: "${DEVEL_NAME:=}" -: "${DEVEL_PACKAGE:=}" -: "${ADMIN_TOOLBAR_NAME:=}" -: "${ADMIN_TOOLBAR_PACKAGE:=}" -: "${COMPOSER_DRUPAL_LENIENT:=}" -: "${DP_CORE_VERSION:=}" -: "${DP_ISSUE_BRANCH:=}" -: "${DP_ISSUE_FORK:=}" -: "${DP_MODULE_VERSION:=}" -: "${DP_PATCH_FILE:=}" - -# Assuming .sh files are in the same directory as this script -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -convert_version() { - local version=$1 - if [[ $version =~ "-" ]]; then - # Remove the part after the dash and replace the last numeric segment with 'x' - local base_version=${version%-*} - echo "${base_version%.*}.x" - else - echo "$version" - fi -} - -# Test cases -# echo $(convert_version "9.2.5-dev1") # Output: 9.2.x -# echo $(convert_version "9.2.5") # Output: 9.2.5 -# echo $(convert_version "10.1.0-beta1") # Output: 10.1.x -# echo $(convert_version "11.0-dev") # Output: 11.x - -# Set a default setup if project type wasn't specified -if [ -z "$DP_PROJECT_TYPE" ]; then - source "$DIR/fallback_setup.sh" -fi - -source "$DIR/git_setup.sh" -# If this is an issue fork of Drupal core - set the drupal core version based on that issue fork -if [ "$DP_PROJECT_TYPE" == "project_core" ] && [ -n "$DP_ISSUE_FORK" ]; then - VERSION_FROM_GIT=$(grep 'const VERSION' "${APP_ROOT}"/repos/drupal/core/lib/Drupal.php | awk -F "'" '{print $2}') - DP_CORE_VERSION=$(convert_version "$VERSION_FROM_GIT") - export DP_CORE_VERSION -fi - -# Measure the time it takes to go through the script -script_start_time=$(date +%s) - -# Remove root-owned files. -sudo rm -rf $APP_ROOT/lost+found - -source "$DIR/contrib_modules_setup.sh" -source "$DIR/cleanup.sh" -source "$DIR/composer_setup.sh" - -if [ -n "$DP_PATCH_FILE" ]; then - echo Applying selected patch "$DP_PATCH_FILE" - cd "${WORK_DIR}" && curl "$DP_PATCH_FILE" | patch -p1 +# Optional debug mode; strict error handling for safety. +if [ -n "${DEBUG_SCRIPT:-}" ]; then + set -x fi +set -eu -o pipefail -# Prepare special setup to work with Drupal core -if [ "$DP_PROJECT_TYPE" == "project_core" ]; then - source "$DIR/drupal_setup_core.sh" -# Prepare special setup to work with Drupal contrib -elif [ -n "$DP_PROJECT_NAME" ]; then - source "$DIR/drupal_setup_contrib.sh" +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Directory Setup (works in both DDEV and GitHub Actions environments) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +DEVPANEL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(dirname "$DEVPANEL_DIR")" +APP_ROOT="${APP_ROOT:-$PROJECT_ROOT}" +DIR="$DEVPANEL_DIR" + +cd "$APP_ROOT" +mkdir -p logs +LOG_FILE="logs/init-$(date +%F-%T).log" +exec > >(tee "$LOG_FILE") 2>&1 + +TIMEFORMAT=%lR +export COMPOSER_NO_AUDIT=1 +export COMPOSER_NO_DEV=1 + +# Drush path (after composer install) +DRUSH="$APP_ROOT/vendor/bin/drush" + +# Source fallback setup +source "$DIR/fallback_setup.sh" + +# Clone AI modules +echo +echo 'Cloning AI modules from git...' +time source "$DIR/clone_ai_modules.sh" + +# Install VSCode extensions +if [ -n "${DP_VSCODE_EXTENSIONS:-}" ]; then + IFS=',' + for value in $DP_VSCODE_EXTENSIONS; do + time code-server --install-extension "$value" + done fi -time "${DIR}"/install-essential-packages.sh -# Configure phpcs for drupal. -cd "$APP_ROOT" && - vendor/bin/phpcs --config-set installed_paths vendor/drupal/coder/coder_sniffer - -if [ -z "$(drush status --field=db-status)" ] || \ - [ $DP_INSTALL_PROFILE != 'demo_umami' ] || \ - ! printf "11.2.2\n$DP_CORE_VERSION" | sort -C; then - # New site install, different install profile, or lower core version. - time drush -n si --account-pass=admin --site-name="DrupalPod" "$DP_INSTALL_PROFILE" -elif [ $DP_CORE_VERSION != '11.2.2' ]; then - # Run database updates if the core version is different. - time drush -n updb +# Clean rebuild vs incremental install +if [ "${DP_REBUILD:-0}" = "1" ]; then + echo + echo 'Performing clean rebuild...' + echo 'Removing docroot directory...' + time rm -rf docroot + echo 'Rebuild mode enabled.' + echo fi -# Install devel and admin_toolbar modules. -if [ "$DP_EXTRA_DEVEL" != '1' ]; then - DEVEL_NAME= -fi -if [ "$DP_EXTRA_ADMIN_TOOLBAR" != '1' ]; then - ADMIN_TOOLBAR_NAME= +# Remove root-owned artifacts +echo +echo "Remove root-owned files." +time sudo rm -rf lost+found + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Composer setup +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +if [ "${DP_REBUILD:-0}" = "1" ] || [ ! -f docroot/composer.json ]; then + source "$DIR/composer_setup.sh" +else + composer show --locked cweagans/composer-patches ^2 &>/dev/null && composer prl fi -# Enable extra modules. -cd "${APP_ROOT}" && - drush en -y \ - $ADMIN_TOOLBAR_NAME \ - $DEVEL_NAME +# Ensure dependencies are installed +composer -n update --no-dev --no-progress || composer dump-autoload -# Enable the requested module. -if [ "$DP_PROJECT_TYPE" == "project_module" ]; then - cd "${APP_ROOT}" && drush en -y "$DP_PROJECT_NAME" -fi +echo 'Running composer update...' +time composer -n update --no-dev --no-progress || { + echo "Composer update encountered errors (likely patch failures), but continuing..." + echo "Regenerating autoload files..." + composer dump-autoload +} -# Enable the requested theme. -if [ "$DP_PROJECT_TYPE" == "project_theme" ]; then - cd "${APP_ROOT}" && drush then -y "$DP_PROJECT_NAME" - cd "${APP_ROOT}" && drush config-set -y system.theme default "$DP_PROJECT_NAME" +echo 'All modules installed and ready!' + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Private files & config +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +[ ! -d private ] && { echo; echo 'Create the private files directory.'; time mkdir private; } +[ ! -d config/sync ] && { echo; echo 'Create the config sync directory.'; time mkdir -p config/sync; } + +# Generate hash salt if missing +[ ! -f "$DIR/salt.txt" ] && { echo; echo 'Generate hash salt.'; time openssl rand -hex 32 > "$DIR/salt.txt"; } + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Install Drupal +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +echo +if [ "${DP_REBUILD:-0}" = "1" ] || ! $DRUSH status --field=bootstrap | grep -q "Drupal bootstrap"; then + PROFILE="${DP_INSTALL_PROFILE:-standard}" + echo "Installing Drupal with profile: $PROFILE" + time $DRUSH -n si "$PROFILE" --account-name=admin --account-pass=admin + + # AI setup if available + if [ -n "${DP_AI_VIRTUAL_KEY:-}" ]; then + source "$DIR/setup_ai.sh" + fi + + echo + echo 'Tell Automatic Updates about patches.' + $DRUSH -n cset --input-format=yaml package_manager.settings additional_trusted_composer_plugins '["cweagans/composer-patches"]' + $DRUSH -n cset --input-format=yaml package_manager.settings additional_known_files_in_project_root '["patches.json", "patches.lock.json"]' + time $DRUSH ev '\Drupal::moduleHandler()->invoke("automatic_updates", "modules_installed", [[], FALSE])' +else + echo 'Update database.' + time $DRUSH -n updb fi -# Finish measuring script time. -script_end_time=$(date +%s) -runtime=$((script_end_time - script_start_time)) -echo "init.sh script ran for" $runtime "seconds" +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Finish measuring script time +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +INIT_DURATION=$SECONDS +INIT_HOURS=$(($INIT_DURATION / 3600)) +INIT_MINUTES=$(($INIT_DURATION % 3600 / 60)) +INIT_SECONDS=$(($INIT_DURATION % 60)) +printf "\nTotal elapsed time: %d:%02d:%02d\n" $INIT_HOURS $INIT_MINUTES $INIT_SECONDS diff --git a/.devpanel/logs/init-2025-12-04-21:55:09.log b/.devpanel/logs/init-2025-12-04-21:55:09.log new file mode 100644 index 00000000..9953a774 --- /dev/null +++ b/.devpanel/logs/init-2025-12-04-21:55:09.log @@ -0,0 +1,631 @@ +📦 AI version will be auto-detected from test module dependencies +📦 AI Module Configuration: + - AI Base: ai @ + - Dependencies: Auto-resolved from composer.json + +Cloning AI modules from git... + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Step 1: No Test Module +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + → Will use latest stable release tag + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Step 2: Clone AI Base Module @ latest stable +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Cloning: ai +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✓ Submodule exists, updating... +Submodule path 'repos/ai': checked out '6c704d8f04e7cb222164897f786fa87bee0f7c95' +0m0.054s + → No version specified, checking out latest stable release + → Found latest stable: 1.2.4 +Previous HEAD position was 6c704d8f Issues/3540486: Translate CKEditor plugin ignores language_source = lang +HEAD is now at b1194e65 Chatbot + → Detected AI version from tag: 1.2.x (from 1.2.4) + ✓ Using detected version for compatibility filtering: 1.2.x + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Step 3: Clone Additional AI Modules (compatibility filtering) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + → Cloning ai_provider_litellm... +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Cloning: ai_provider_litellm +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✓ Submodule exists, updating... +Submodule path 'repos/ai_provider_litellm': checked out '7523d5cac87cd831a480b411bc0eb7aca03f82f1' +0m0.032s + → No version specified, checking out latest stable release + → Found latest stable: 1.2.0-rc1 +Previous HEAD position was 7523d5c 3558458: add extra check on handleApiException +HEAD is now at 2abcc2e Resolve #3550655 "Override handleapiexception to" + ✓ ai_provider_litellm is compatible with AI 1.2.x + → Cloning ai_search... +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Cloning: ai_search +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✓ Submodule exists, updating... +0m0.021s + → No version specified, checking out latest stable release + → Found latest stable: 2.0.0-alpha1 +HEAD is now at 43a6627 fix: #3557904 Vector DB Explorer fails with TypeError when search results contain array values in extra data + ⚠️ Incompatible: requires AI ^2.0, but we have AI 1.2.x + ✗ Skipping ai_search from composer (incompatible, but cloned for inspection) + → Cloning ai_agents... +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Cloning: ai_agents +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✓ Submodule exists, updating... +Submodule path 'repos/ai_agents': checked out '9502f73f218999de66050463f1182fa51c6c0714' +0m0.030s + → No version specified, checking out latest stable release + → Found latest stable: 1.2.1 +Previous HEAD position was 9502f73 Cherry pick code from #3536318 +HEAD is now at 2fe31d6 Cherry pick code from #3536318 + ✓ ai_agents is compatible with AI 1.2.x + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Step 4: Clone Default Provider +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✓ Provider already cloned + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +✓ Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + AI Version: 1.2.x + Cloned modules: ai,ai_provider_litellm,ai_search,ai_agents + Compatible (will be added to composer): ai,ai_provider_litellm,ai_agents +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +0m6.817s + +Performing clean rebuild... +Removing docroot directory... +0m0.993s +Rebuild mode enabled. + + +Remove root-owned files. +0m0.007s + +Generate composer.json. +Creating a "drupal/cms:2.0.x-dev" project at "./temp-composer-files" +Installing drupal/cms (2.0.x-dev 58e1bbb0d3740edf197985aaae25c6569714fa87) + - Installing drupal/cms (2.0.x-dev 58e1bbb): Extracting archive +Created project in /var/www/html/docroot/docroot/temp-composer-files +0m0.453s +Adding path repositories for compatible AI modules... +Path repositories added for compatible AI modules! +Adding CMS dependencies (full setup with webform libraries)... +Using version ^5.65 for codemirror/codemirror +Using version ^5.0 for jquery/inputmask +Using version ^17.0 for jquery/intl-tel-input +Using version ^1.1 for jquery/rateit +Using version ^4.0 for jquery/select2 +Using version ^0.9.1 for jquery/textcounter +Using version ^1.14 for jquery/timepicker +Using version ^2.11 for popperjs/popperjs +Using version ^2.0 for progress-tracker/progress-tracker +Using version ^2.3 for signature_pad/signature_pad +Using version ^12.0 for tabby/tabby +Using version ^6.3 for tippyjs/tippyjs +drush/drush is currently present in the require-dev key and you ran the command without the --dev flag, which will move it to the require key. +./composer.json has been updated +0m4.359s + +Running composer update... +Loading composer repositories with package information +Updating dependencies +Lock file operations: 225 installs, 0 updates, 0 removals + - Locking asm89/stack-cors (v2.3.0) + - Locking chi-teck/drupal-code-generator (4.2.0) + - Locking codemirror/codemirror (5.65.12) + - Locking composer/ca-bundle (1.5.9) + - Locking composer/class-map-generator (1.7.0) + - Locking composer/composer (2.9.2) + - Locking composer/installers (v2.3.0) + - Locking composer/metadata-minifier (1.0.0) + - Locking composer/pcre (3.3.2) + - Locking composer/semver (3.4.4) + - Locking composer/spdx-licenses (1.5.9) + - Locking composer/xdebug-handler (3.0.5) + - Locking consolidation/annotated-command (4.10.4) + - Locking consolidation/config (3.2.0) + - Locking consolidation/filter-via-dot-access-data (2.0.3) + - Locking consolidation/log (3.1.1) + - Locking consolidation/output-formatters (4.7.0) + - Locking consolidation/robo (5.1.1) + - Locking consolidation/site-alias (4.1.2) + - Locking consolidation/site-process (5.4.2) + - Locking cweagans/composer-configurable-plugin (2.0.0) + - Locking cweagans/composer-patches (2.0.0) + - Locking dflydev/dot-access-data (v3.0.3) + - Locking doctrine/annotations (2.0.2) + - Locking doctrine/deprecations (1.1.5) + - Locking doctrine/lexer (2.1.1) + - Locking dragonmantank/cron-expression (v3.6.0) + - Locking drupal/ai (1.2.4) + - Locking drupal/ai_agents (1.2.1) + - Locking drupal/ai_image_alt_text (1.0.1) + - Locking drupal/ai_provider_anthropic (1.2.0) + - Locking drupal/ai_provider_openai (1.2.0) + - Locking drupal/automatic_updates (4.0.0) + - Locking drupal/autosave_form (1.10.0) + - Locking drupal/better_exposed_filters (7.1.1) + - Locking drupal/bpmn_io (3.0.2) + - Locking drupal/byte (1.0.0-alpha5) + - Locking drupal/canvas (1.0.0) + - Locking drupal/captcha (2.0.9) + - Locking drupal/checklistapi (2.1.7) + - Locking drupal/coffee (2.0.1) + - Locking drupal/core (11.2.9) + - Locking drupal/core-composer-scaffold (11.2.9) + - Locking drupal/core-project-message (11.2.9) + - Locking drupal/core-recipe-unpack (11.2.9) + - Locking drupal/core-recommended (11.2.9) + - Locking drupal/core-vendor-hardening (11.2.9) + - Locking drupal/crop (2.5.0) + - Locking drupal/ctools (4.1.0) + - Locking drupal/cva (1.0.0-beta1) + - Locking drupal/dashboard (2.1.1) + - Locking drupal/drupal_cms_accessibility_tools (2.0.0-alpha2) + - Locking drupal/drupal_cms_admin_ui (2.0.0-alpha2) + - Locking drupal/drupal_cms_ai (2.0.0-alpha2) + - Locking drupal/drupal_cms_anti_spam (2.0.0-alpha2) + - Locking drupal/drupal_cms_authentication (2.0.0-alpha2) + - Locking drupal/drupal_cms_blog (2.0.0-alpha2) + - Locking drupal/drupal_cms_content_type_base (2.0.0-alpha2) + - Locking drupal/drupal_cms_forms (2.0.0-alpha2) + - Locking drupal/drupal_cms_google_analytics (2.0.0-alpha2) + - Locking drupal/drupal_cms_helper (2.0.0-alpha2) + - Locking drupal/drupal_cms_image (2.0.0-alpha2) + - Locking drupal/drupal_cms_page (2.0.0-alpha2) + - Locking drupal/drupal_cms_privacy_basic (2.0.0-alpha2) + - Locking drupal/drupal_cms_remote_video (2.0.0-alpha2) + - Locking drupal/drupal_cms_search (2.0.0-alpha2) + - Locking drupal/drupal_cms_seo_basic (2.0.0-alpha2) + - Locking drupal/drupal_cms_seo_tools (2.0.0-alpha2) + - Locking drupal/drupal_cms_starter (2.0.0-alpha2) + - Locking drupal/easy_breadcrumb (2.0.9) + - Locking drupal/easy_email (3.0.7) + - Locking drupal/easy_email_express (1.0.4) + - Locking drupal/easy_email_standard (1.0.3) + - Locking drupal/easy_email_text_format (1.0.3) + - Locking drupal/easy_email_theme (1.1.0) + - Locking drupal/easy_email_types_core (1.0.4) + - Locking drupal/easy_email_types_default (1.0.2) + - Locking drupal/eca (3.0.8) + - Locking drupal/editoria11y (2.2.18) + - Locking drupal/field_group (4.0.0) + - Locking drupal/focal_point (2.1.2) + - Locking drupal/friendly_captcha_challenge (0.9.18) + - Locking drupal/friendlycaptcha (1.1.4) + - Locking drupal/gin (5.0.9) + - Locking drupal/gin_login (2.1.3) + - Locking drupal/gin_toolbar (3.0.2) + - Locking drupal/google_tag (2.0.9) + - Locking drupal/honeypot (2.2.2) + - Locking drupal/jquery_ui (1.8.0) + - Locking drupal/jquery_ui_resizable (2.1.0) + - Locking drupal/key (1.20.0) + - Locking drupal/klaro (3.0.7) + - Locking drupal/klaro_js (3.0.1) + - Locking drupal/linkit (7.0.11) + - Locking drupal/login_emailusername (3.0.1) + - Locking drupal/mailsystem (4.5.0) + - Locking drupal/menu_link_attributes (1.5.0) + - Locking drupal/mercury (1.0.0-alpha2) + - Locking drupal/metatag (2.2.0) + - Locking drupal/modeler_api (1.0.6) + - Locking drupal/nouislider_js (15.8.1) + - Locking drupal/pathauto (1.14.0) + - Locking drupal/project_browser (2.1.2) + - Locking drupal/recipe_installer_kit (1.0.0) + - Locking drupal/redirect (1.12.0) + - Locking drupal/robotstxt (1.6.0) + - Locking drupal/sam (1.3.2) + - Locking drupal/scheduler (2.2.2) + - Locking drupal/scheduler_content_moderation_integration (3.0.4) + - Locking drupal/search_api (1.40.0) + - Locking drupal/search_api_autocomplete (1.11.0) + - Locking drupal/search_api_exclude (2.0.3) + - Locking drupal/selective_better_exposed_filters (3.0.3) + - Locking drupal/seo_checklist (5.2.4) + - Locking drupal/simple_search_form (1.8.0) + - Locking drupal/simple_sitemap (4.2.3) + - Locking drupal/sitemap (2.5.0) + - Locking drupal/svg_image (3.2.2) + - Locking drupal/symfony_mailer_lite (2.0.4) + - Locking drupal/tagify (1.2.44) + - Locking drupal/token (1.16.0) + - Locking drupal/token_or (2.3.2) + - Locking drupal/trash (3.0.22) + - Locking drupal/ui_icons (1.1.0-beta6) + - Locking drupal/webform (6.3.0-beta6) + - Locking drupal/yoast_seo (2.2.0) + - Locking drush/drush (13.7.0) + - Locking egulias/email-validator (4.0.4) + - Locking enshrined/svg-sanitize (0.22.0) + - Locking goalgorilla/rtseo.js (2.1.0) + - Locking grasmash/expander (3.0.1) + - Locking grasmash/yaml-cli (3.2.1) + - Locking guzzlehttp/guzzle (7.9.3) + - Locking guzzlehttp/promises (2.2.0) + - Locking guzzlehttp/psr7 (2.7.1) + - Locking html2text/html2text (4.3.2) + - Locking jquery/inputmask (5.0.9) + - Locking jquery/intl-tel-input (17.0.19) + - Locking jquery/rateit (1.1.5) + - Locking jquery/select2 (4.0.13) + - Locking jquery/textcounter (0.9.1) + - Locking jquery/timepicker (1.14.0) + - Locking justinrainbow/json-schema (6.6.3) + - Locking laravel/prompts (v0.3.8) + - Locking league/commonmark (2.8.0) + - Locking league/config (v1.2.0) + - Locking league/container (4.2.5) + - Locking league/html-to-markdown (5.1.1) + - Locking marc-mabe/php-enum (v4.7.2) + - Locking masterminds/html5 (2.9.0) + - Locking mck89/peast (v1.17.4) + - Locking mtownsend/xml-to-array (2.0.0) + - Locking nette/schema (v1.3.3) + - Locking nette/utils (v4.1.0) + - Locking nikic/php-parser (v5.6.2) + - Locking openai-php/client (v0.18.0) + - Locking pear/archive_tar (1.5.0) + - Locking pear/console_getopt (v1.4.3) + - Locking pear/pear-core-minimal (v1.10.16) + - Locking pear/pear_exception (v1.0.2) + - Locking phootwork/collection (v3.2.3) + - Locking phootwork/lang (v3.2.3) + - Locking php-http/discovery (1.20.0) + - Locking php-http/multipart-stream-builder (1.4.2) + - Locking php-tuf/composer-stager (v2.0.2) + - Locking phpowermove/docblock (v4.0) + - Locking popperjs/popperjs (2.11.6) + - Locking progress-tracker/progress-tracker (2.0.7) + - Locking psr/cache (3.0.0) + - Locking psr/container (2.0.2) + - Locking psr/event-dispatcher (1.0.0) + - Locking psr/http-client (1.0.3) + - Locking psr/http-factory (1.1.0) + - Locking psr/http-message (2.0) + - Locking psr/log (3.0.2) + - Locking psy/psysh (v0.12.15) + - Locking ralouphie/getallheaders (3.0.3) + - Locking react/promise (v3.3.0) + - Locking revolt/event-loop (v1.0.8) + - Locking sebastian/diff (7.0.0) + - Locking seld/jsonlint (1.11.0) + - Locking seld/phar-utils (1.2.1) + - Locking seld/signal-handler (2.0.2) + - Locking signature_pad/signature_pad (2.3.0) + - Locking symfony/console (v7.3.6) + - Locking symfony/css-selector (v7.4.0) + - Locking symfony/dependency-injection (v7.3.6) + - Locking symfony/deprecation-contracts (v3.6.0) + - Locking symfony/error-handler (v7.3.6) + - Locking symfony/event-dispatcher (v7.3.3) + - Locking symfony/event-dispatcher-contracts (v3.6.0) + - Locking symfony/filesystem (v7.3.6) + - Locking symfony/finder (v7.3.5) + - Locking symfony/http-foundation (v7.3.7) + - Locking symfony/http-kernel (v7.3.7) + - Locking symfony/mailer (v7.3.5) + - Locking symfony/mime (v7.3.4) + - Locking symfony/polyfill-ctype (v1.32.0) + - Locking symfony/polyfill-iconv (v1.32.0) + - Locking symfony/polyfill-intl-grapheme (v1.32.0) + - Locking symfony/polyfill-intl-idn (v1.32.0) + - Locking symfony/polyfill-intl-normalizer (v1.32.0) + - Locking symfony/polyfill-mbstring (v1.32.0) + - Locking symfony/polyfill-php73 (v1.33.0) + - Locking symfony/polyfill-php80 (v1.33.0) + - Locking symfony/polyfill-php81 (v1.33.0) + - Locking symfony/polyfill-php83 (v1.33.0) + - Locking symfony/polyfill-php84 (v1.32.0) + - Locking symfony/process (v7.3.4) + - Locking symfony/psr-http-message-bridge (v7.3.0) + - Locking symfony/routing (v7.3.6) + - Locking symfony/serializer (v7.3.5) + - Locking symfony/service-contracts (v3.6.1) + - Locking symfony/string (v7.3.4) + - Locking symfony/translation-contracts (v3.6.1) + - Locking symfony/validator (v7.3.7) + - Locking symfony/var-dumper (v7.3.5) + - Locking symfony/var-exporter (v7.3.4) + - Locking symfony/yaml (v7.3.5) + - Locking tabby/tabby (12.0.3) + - Locking tijsverkoyen/css-to-inline-styles (v2.3.0) + - Locking tippyjs/tippyjs (6.3.7) + - Locking twig/html-extra (v3.22.1) + - Locking twig/twig (v3.21.1) + - Locking yethee/tiktoken (0.5.1) +Writing lock file +Installing dependencies from lock file +Package operations: 225 installs, 0 updates, 0 removals + - Installing cweagans/composer-configurable-plugin (2.0.0): Extracting archive + - Installing cweagans/composer-patches (2.0.0): Extracting archive +patches.lock.json does not exist. Creating a new patches.lock.json. + - Resolving patches from dependencies. + - Installing composer/installers (v2.3.0): Extracting archive + - Installing drupal/core-composer-scaffold (11.2.9): Extracting archive + - Installing drupal/core-project-message (11.2.9): Extracting archive + - Installing drupal/core-recipe-unpack (11.2.9): Extracting archive + - Installing drupal/core-vendor-hardening (11.2.9): Extracting archive + - Installing php-http/discovery (1.20.0): Extracting archive + - Installing codemirror/codemirror (5.65.12): Extracting archive + - Installing symfony/process (v7.3.4): Extracting archive + - Installing symfony/polyfill-php84 (v1.32.0): Extracting archive + - Installing symfony/polyfill-php81 (v1.33.0): Extracting archive + - Installing symfony/polyfill-php80 (v1.33.0): Extracting archive + - Installing symfony/polyfill-php73 (v1.33.0): Extracting archive + - Installing symfony/finder (v7.3.5): Extracting archive + - Installing symfony/polyfill-iconv (v1.32.0): Extracting archive + - Installing symfony/polyfill-mbstring (v1.32.0): Extracting archive + - Installing symfony/polyfill-ctype (v1.32.0): Extracting archive + - Installing symfony/filesystem (v7.3.6): Extracting archive + - Installing symfony/polyfill-intl-normalizer (v1.32.0): Extracting archive + - Installing symfony/polyfill-intl-grapheme (v1.32.0): Extracting archive + - Installing symfony/string (v7.3.4): Extracting archive + - Installing symfony/deprecation-contracts (v3.6.0): Extracting archive + - Installing psr/container (2.0.2): Extracting archive + - Installing symfony/service-contracts (v3.6.1): Extracting archive + - Installing symfony/console (v7.3.6): Extracting archive + - Installing seld/signal-handler (2.0.2): Extracting archive + - Installing seld/phar-utils (1.2.1): Extracting archive + - Installing seld/jsonlint (1.11.0): Extracting archive + - Installing react/promise (v3.3.0): Extracting archive + - Installing psr/log (3.0.2): Extracting archive + - Installing marc-mabe/php-enum (v4.7.2): Extracting archive + - Installing justinrainbow/json-schema (6.6.3): Extracting archive + - Installing composer/pcre (3.3.2): Extracting archive + - Installing composer/xdebug-handler (3.0.5): Extracting archive + - Installing composer/spdx-licenses (1.5.9): Extracting archive + - Installing composer/semver (3.4.4): Extracting archive + - Installing composer/metadata-minifier (1.0.0): Extracting archive + - Installing composer/class-map-generator (1.7.0): Extracting archive + - Installing composer/ca-bundle (1.5.9): Extracting archive + - Installing composer/composer (2.9.2): Extracting archive + - Installing consolidation/log (3.1.1): Extracting archive + - Installing twig/twig (v3.21.1): Extracting archive + - Installing symfony/yaml (v7.3.5): Extracting archive + - Installing symfony/translation-contracts (v3.6.1): Extracting archive + - Installing symfony/polyfill-php83 (v1.33.0): Extracting archive + - Installing symfony/validator (v7.3.7): Extracting archive + - Installing symfony/serializer (v7.3.5): Extracting archive + - Installing symfony/routing (v7.3.6): Extracting archive + - Installing symfony/http-foundation (v7.3.7): Extracting archive + - Installing psr/http-message (2.0): Extracting archive + - Installing symfony/psr-http-message-bridge (v7.3.0): Extracting archive + - Installing symfony/polyfill-intl-idn (v1.32.0): Extracting archive + - Installing symfony/mime (v7.3.4): Extracting archive + - Installing psr/event-dispatcher (1.0.0): Extracting archive + - Installing symfony/event-dispatcher-contracts (v3.6.0): Extracting archive + - Installing symfony/event-dispatcher (v7.3.3): Extracting archive + - Installing doctrine/deprecations (1.1.5): Extracting archive + - Installing doctrine/lexer (2.1.1): Extracting archive + - Installing egulias/email-validator (4.0.4): Extracting archive + - Installing symfony/mailer (v7.3.5): Extracting archive + - Installing symfony/var-dumper (v7.3.5): Extracting archive + - Installing symfony/error-handler (v7.3.6): Extracting archive + - Installing symfony/http-kernel (v7.3.7): Extracting archive + - Installing symfony/var-exporter (v7.3.4): Extracting archive + - Installing symfony/dependency-injection (v7.3.6): Extracting archive + - Installing sebastian/diff (7.0.0): Extracting archive + - Installing revolt/event-loop (v1.0.8): Extracting archive + - Installing php-tuf/composer-stager (v2.0.2): Extracting archive + - Installing pear/pear_exception (v1.0.2): Extracting archive + - Installing pear/console_getopt (v1.4.3): Extracting archive + - Installing pear/pear-core-minimal (v1.10.16): Extracting archive + - Installing pear/archive_tar (1.5.0): Extracting archive + - Installing mck89/peast (v1.17.4): Extracting archive + - Installing masterminds/html5 (2.9.0): Extracting archive + - Installing ralouphie/getallheaders (3.0.3): Extracting archive + - Installing psr/http-factory (1.1.0): Extracting archive + - Installing guzzlehttp/psr7 (2.7.1): Extracting archive + - Installing psr/http-client (1.0.3): Extracting archive + - Installing guzzlehttp/promises (2.2.0): Extracting archive + - Installing guzzlehttp/guzzle (7.9.3): Extracting archive + - Installing psr/cache (3.0.0): Extracting archive + - Installing doctrine/annotations (2.0.2): Extracting archive + - Installing asm89/stack-cors (v2.3.0): Extracting archive + - Installing drupal/core (11.2.9): Extracting archive + - Installing drupal/ui_icons (1.1.0-beta6): Extracting archive + - Installing drupal/mercury (1.0.0-alpha2): Extracting archive + - Installing drupal/easy_email_text_format (1.0.3): Extracting archive + - Installing drupal/token (1.16.0): Extracting archive + - Installing drupal/jquery_ui (1.8.0): Extracting archive + - Installing drupal/jquery_ui_resizable (2.1.0): Extracting archive + - Installing drupal/easy_email (3.0.7): Extracting archive + - Installing drupal/easy_email_types_default (1.0.2): Extracting archive + - Installing drupal/easy_email_types_core (1.0.4): Extracting archive + - Installing symfony/css-selector (v7.4.0): Extracting archive + - Installing tijsverkoyen/css-to-inline-styles (v2.3.0): Extracting archive + - Installing html2text/html2text (4.3.2): Extracting archive + - Installing drupal/mailsystem (4.5.0): Extracting archive + - Installing drupal/symfony_mailer_lite (2.0.4): Extracting archive + - Installing drupal/easy_email_theme (1.1.0): Extracting archive + - Installing drupal/easy_email_standard (1.0.3): Extracting archive + - Installing drupal/easy_email_express (1.0.4): Extracting archive + - Installing goalgorilla/rtseo.js (2.1.0): Extracting archive + - Installing drupal/metatag (2.2.0): Extracting archive + - Installing drupal/yoast_seo (2.2.0): Extracting archive + - Installing drupal/token_or (2.3.2): Extracting archive + - Installing drupal/sitemap (2.5.0): Extracting archive + - Installing drupal/simple_sitemap (4.2.3): Extracting archive + - Installing drupal/checklistapi (2.1.7): Extracting archive + - Installing drupal/seo_checklist (5.2.4): Extracting archive + - Installing drupal/robotstxt (1.6.0): Extracting archive + - Installing drupal/modeler_api (1.0.6): Extracting archive + - Installing drupal/crop (2.5.0): Extracting archive + - Installing drupal/focal_point (2.1.2): Extracting archive + - Installing drupal/field_group (4.0.0): Extracting archive + - Installing dragonmantank/cron-expression (v3.6.0): Extracting archive + - Installing drupal/eca (3.0.8): Extracting archive + - Installing mtownsend/xml-to-array (2.0.0): Extracting archive + - Installing drupal/bpmn_io (3.0.2): Extracting archive + - Installing drupal/drupal_cms_seo_tools (2.0.0-alpha2): Extracting archive + - Installing drupal/menu_link_attributes (1.5.0): Extracting archive + - Installing drupal/klaro_js (3.0.1): Extracting archive + - Installing drupal/klaro (3.0.7): Extracting archive + - Installing drupal/trash (3.0.22): Extracting archive + - Installing drupal/tagify (1.2.44): Extracting archive + - Installing drupal/scheduler (2.2.2): Extracting archive + - Installing drupal/scheduler_content_moderation_integration (3.0.4): Extracting archive + - Installing drupal/ctools (4.1.0): Extracting archive + - Installing drupal/pathauto (1.14.0): Extracting archive + - Installing drupal/linkit (7.0.11): Extracting archive + - Installing enshrined/svg-sanitize (0.22.0): Extracting archive + - Installing drupal/svg_image (3.2.2): Extracting archive + - Installing drupal/drupal_cms_image (2.0.0-alpha2): Extracting archive + - Installing drupal/drupal_cms_helper (2.0.0-alpha2): Extracting archive + - Installing drupal/canvas (1.0.0): Extracting archive + - Installing drupal/autosave_form (1.10.0): Extracting archive + - Installing drupal/drupal_cms_content_type_base (2.0.0-alpha2): Extracting archive + - Installing drupal/drupal_cms_page (2.0.0-alpha2): Extracting archive + - Installing drupal/drupal_cms_privacy_basic (2.0.0-alpha2): Extracting archive + - Installing drupal/drupal_cms_remote_video (2.0.0-alpha2): Extracting archive + - Installing drupal/webform (6.3.0-beta6): Extracting archive + - Installing drupal/honeypot (2.2.2): Extracting archive + - Installing drupal/captcha (2.0.9): Extracting archive + - Installing drupal/friendlycaptcha (1.1.4): Extracting archive + - Installing drupal/friendly_captcha_challenge (0.9.18): Extracting archive + - Installing drupal/drupal_cms_anti_spam (2.0.0-alpha2): Extracting archive + - Installing drupal/drupal_cms_forms (2.0.0-alpha2): Extracting archive + - Installing drupal/nouislider_js (15.8.1): Extracting archive + - Installing drupal/better_exposed_filters (7.1.1): Extracting archive + - Installing drupal/selective_better_exposed_filters (3.0.3): Extracting archive + - Installing drupal/drupal_cms_blog (2.0.0-alpha2): Extracting archive + - Installing drupal/login_emailusername (3.0.1): Extracting archive + - Installing drupal/drupal_cms_authentication (2.0.0-alpha2): Extracting archive + - Installing drupal/sam (1.3.2): Extracting archive + - Installing drupal/project_browser (2.1.2): Extracting archive + - Installing drupal/gin_login (2.1.3): Extracting archive + - Installing drupal/gin_toolbar (3.0.2): Extracting archive + - Installing drupal/gin (5.0.9): Extracting archive + - Installing drupal/dashboard (2.1.1): Extracting archive + - Installing drupal/coffee (2.0.1): Extracting archive + - Installing drupal/drupal_cms_admin_ui (2.0.0-alpha2): Extracting archive + - Installing drupal/byte (1.0.0-alpha5): Extracting archive + - Installing drupal/core-recommended (11.2.9) + - Installing twig/html-extra (v3.22.1): Extracting archive + - Installing drupal/cva (1.0.0-beta1): Extracting archive + - Installing drupal/editoria11y (2.2.18): Extracting archive + - Installing drupal/drupal_cms_accessibility_tools (2.0.0-alpha2): Extracting archive + - Installing nette/utils (v4.1.0): Extracting archive + - Installing nette/schema (v1.3.3): Extracting archive + - Installing dflydev/dot-access-data (v3.0.3): Extracting archive + - Installing league/config (v1.2.0): Extracting archive + - Installing league/commonmark (2.8.0): Extracting archive + - Installing drupal/key (1.20.0): Extracting archive + - Installing yethee/tiktoken (0.5.1): Extracting archive + - Installing php-http/multipart-stream-builder (1.4.2): Extracting archive + - Installing openai-php/client (v0.18.0): Extracting archive + - Installing league/html-to-markdown (5.1.1): Extracting archive + - Installing drupal/ai (1.2.4): Extracting archive + - Installing drupal/ai_provider_openai (1.2.0): Extracting archive + - Installing drupal/ai_provider_anthropic (1.2.0): Extracting archive + - Installing drupal/ai_image_alt_text (1.0.1): Extracting archive + - Installing drupal/ai_agents (1.2.1): Extracting archive + - Installing drupal/drupal_cms_ai (2.0.0-alpha2): Extracting archive + - Installing drupal/google_tag (2.0.9): Extracting archive + - Installing drupal/drupal_cms_google_analytics (2.0.0-alpha2): Extracting archive + - Installing drupal/redirect (1.12.0): Extracting archive + - Installing drupal/easy_breadcrumb (2.0.9): Extracting archive + - Installing drupal/drupal_cms_seo_basic (2.0.0-alpha2): Extracting archive + - Installing drupal/simple_search_form (1.8.0): Extracting archive + - Installing drupal/search_api (1.40.0): Extracting archive + - Installing drupal/search_api_exclude (2.0.3): Extracting archive + - Installing drupal/search_api_autocomplete (1.11.0): Extracting archive + - Installing drupal/drupal_cms_search (2.0.0-alpha2): Extracting archive + - Installing drupal/automatic_updates (4.0.0): Extracting archive + - Installing drupal/drupal_cms_starter (2.0.0-alpha2): Extracting archive + - Installing nikic/php-parser (v5.6.2): Extracting archive + - Installing psy/psysh (v0.12.15): Extracting archive + - Installing league/container (4.2.5): Extracting archive + - Installing laravel/prompts (v0.3.8): Extracting archive + - Installing grasmash/yaml-cli (3.2.1): Extracting archive + - Installing grasmash/expander (3.0.1): Extracting archive + - Installing consolidation/config (3.2.0): Extracting archive + - Installing consolidation/site-alias (4.1.2): Extracting archive + - Installing consolidation/site-process (5.4.2): Extracting archive + - Installing phootwork/lang (v3.2.3): Extracting archive + - Installing phootwork/collection (v3.2.3): Extracting archive + - Installing phpowermove/docblock (v4.0): Extracting archive + - Installing consolidation/output-formatters (4.7.0): Extracting archive + - Installing consolidation/annotated-command (4.10.4): Extracting archive + - Installing consolidation/robo (5.1.1): Extracting archive + - Installing consolidation/filter-via-dot-access-data (2.0.3): Extracting archive + - Installing chi-teck/drupal-code-generator (4.2.0): Extracting archive + - Installing drush/drush (13.7.0): Extracting archive + - Installing drupal/recipe_installer_kit (1.0.0): Extracting archive + - Installing jquery/inputmask (5.0.9): Extracting archive + - Installing jquery/intl-tel-input (17.0.19): Extracting archive + - Installing jquery/rateit (1.1.5): Extracting archive + - Installing jquery/select2 (4.0.13): Extracting archive + - Installing jquery/textcounter (0.9.1): Extracting archive + - Installing jquery/timepicker (1.14.0): Extracting archive + - Installing popperjs/popperjs (2.11.6): Extracting archive + - Installing progress-tracker/progress-tracker (2.0.7): Extracting archive + - Installing signature_pad/signature_pad (2.3.0): Extracting archive + - Installing tabby/tabby (12.0.3): Extracting archive + - Installing tippyjs/tippyjs (6.3.7): Extracting archive + Cleaning: symfony/process + Cleaning: symfony/finder + Cleaning: symfony/filesystem + Cleaning: symfony/console + Cleaning: seld/jsonlint + Cleaning: justinrainbow/json-schema + Cleaning: twig/twig + Cleaning: symfony/yaml + Cleaning: symfony/validator + Cleaning: symfony/serializer + Cleaning: symfony/routing + Cleaning: symfony/http-foundation + Cleaning: symfony/psr-http-message-bridge + Cleaning: symfony/event-dispatcher + Cleaning: egulias/email-validator + Cleaning: symfony/var-dumper + Cleaning: symfony/http-kernel + Cleaning: symfony/dependency-injection + Cleaning: sebastian/diff + Cleaning: pear/pear_exception + Cleaning: pear/console_getopt + Cleaning: pear/pear-core-minimal + Cleaning: pear/archive_tar + Cleaning: mck89/peast + Cleaning: masterminds/html5 + Cleaning: guzzlehttp/psr7 + Cleaning: guzzlehttp/promises + Cleaning: symfony/css-selector + - Patching drupal/canvas + - Found cached patch at /mnt/ddev-global-cache/composer/patches/dd1ee073adaa9b96b834661b2aa6781620cbbf87ae95be5f58bc2e568b92f9bd.patch + +In Patches.php line 288: + + No available patcher was able to apply patch https://git.drupalcode.org/project/canvas/-/merge_requests/187.diff to dr + upal/canvas + + +update [--with WITH] [--prefer-source] [--prefer-dist] [--prefer-install PREFER-INSTALL] [--dry-run] [--dev] [--no-dev] [--lock] [--no-install] [--no-audit] [--audit-format AUDIT-FORMAT] [--no-security-blocking] [--no-autoloader] [--no-suggest] [--no-progress] [-w|--with-dependencies] [-W|--with-all-dependencies] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--apcu-autoloader-prefix APCU-AUTOLOADER-PREFIX] [--ignore-platform-req IGNORE-PLATFORM-REQ] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [-m|--minimal-changes] [--patch-only] [-i|--interactive] [--root-reqs] [--bump-after-update [BUMP-AFTER-UPDATE]] [--] [...] + +0m17.587s +Composer update encountered errors (likely patch failures), but continuing... +Regenerating autoload files... +Generating optimized autoload files +Hardening vendor directory with .htaccess file. +Generated optimized autoload files containing 7393 classes +All modules installed and ready! + +Create the private files directory. +0m0.003s + +Create the config sync directory. +0m0.001s + +Install Drupal. +drush is not available. You may need to 'ddev composer require drush/drush' +0m0.038s diff --git a/.devpanel/logs/init-2025-12-04-21:55:58.log b/.devpanel/logs/init-2025-12-04-21:55:58.log new file mode 100644 index 00000000..79589bb0 --- /dev/null +++ b/.devpanel/logs/init-2025-12-04-21:55:58.log @@ -0,0 +1,631 @@ +📦 AI version will be auto-detected from test module dependencies +📦 AI Module Configuration: + - AI Base: ai @ + - Dependencies: Auto-resolved from composer.json + +Cloning AI modules from git... + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Step 1: No Test Module +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + → Will use latest stable release tag + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Step 2: Clone AI Base Module @ latest stable +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Cloning: ai +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✓ Submodule exists, updating... +Submodule path 'repos/ai': checked out '6c704d8f04e7cb222164897f786fa87bee0f7c95' +0m0.059s + → No version specified, checking out latest stable release + → Found latest stable: 1.2.4 +Previous HEAD position was 6c704d8f Issues/3540486: Translate CKEditor plugin ignores language_source = lang +HEAD is now at b1194e65 Chatbot + → Detected AI version from tag: 1.2.x (from 1.2.4) + ✓ Using detected version for compatibility filtering: 1.2.x + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Step 3: Clone Additional AI Modules (compatibility filtering) +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + → Cloning ai_provider_litellm... +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Cloning: ai_provider_litellm +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✓ Submodule exists, updating... +Submodule path 'repos/ai_provider_litellm': checked out '7523d5cac87cd831a480b411bc0eb7aca03f82f1' +0m0.029s + → No version specified, checking out latest stable release + → Found latest stable: 1.2.0-rc1 +Previous HEAD position was 7523d5c 3558458: add extra check on handleApiException +HEAD is now at 2abcc2e Resolve #3550655 "Override handleapiexception to" + ✓ ai_provider_litellm is compatible with AI 1.2.x + → Cloning ai_search... +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Cloning: ai_search +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✓ Submodule exists, updating... +0m0.021s + → No version specified, checking out latest stable release + → Found latest stable: 2.0.0-alpha1 +HEAD is now at 43a6627 fix: #3557904 Vector DB Explorer fails with TypeError when search results contain array values in extra data + ⚠️ Incompatible: requires AI ^2.0, but we have AI 1.2.x + ✗ Skipping ai_search from composer (incompatible, but cloned for inspection) + → Cloning ai_agents... +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Cloning: ai_agents +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✓ Submodule exists, updating... +Submodule path 'repos/ai_agents': checked out '9502f73f218999de66050463f1182fa51c6c0714' +0m0.027s + → No version specified, checking out latest stable release + → Found latest stable: 1.2.1 +Previous HEAD position was 9502f73 Cherry pick code from #3536318 +HEAD is now at 2fe31d6 Cherry pick code from #3536318 + ✓ ai_agents is compatible with AI 1.2.x + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +Step 4: Clone Default Provider +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ✓ Provider already cloned + +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +✓ Summary +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + AI Version: 1.2.x + Cloned modules: ai,ai_provider_litellm,ai_search,ai_agents + Compatible (will be added to composer): ai,ai_provider_litellm,ai_agents +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +0m6.081s + +Performing clean rebuild... +Removing docroot directory... +0m0.786s +Rebuild mode enabled. + + +Remove root-owned files. +0m0.006s + +Generate composer.json. +Creating a "drupal/cms:2.0.x-dev" project at "./temp-composer-files" +Installing drupal/cms (2.0.x-dev 58e1bbb0d3740edf197985aaae25c6569714fa87) + - Installing drupal/cms (2.0.x-dev 58e1bbb): Extracting archive +Created project in /var/www/html/docroot/docroot/temp-composer-files +0m0.475s +Adding path repositories for compatible AI modules... +Path repositories added for compatible AI modules! +Adding CMS dependencies (full setup with webform libraries)... +Using version ^5.65 for codemirror/codemirror +Using version ^5.0 for jquery/inputmask +Using version ^17.0 for jquery/intl-tel-input +Using version ^1.1 for jquery/rateit +Using version ^4.0 for jquery/select2 +Using version ^0.9.1 for jquery/textcounter +Using version ^1.14 for jquery/timepicker +Using version ^2.11 for popperjs/popperjs +Using version ^2.0 for progress-tracker/progress-tracker +Using version ^2.3 for signature_pad/signature_pad +Using version ^12.0 for tabby/tabby +Using version ^6.3 for tippyjs/tippyjs +drush/drush is currently present in the require-dev key and you ran the command without the --dev flag, which will move it to the require key. +./composer.json has been updated +0m4.688s + +Running composer update... +Loading composer repositories with package information +Updating dependencies +Lock file operations: 225 installs, 0 updates, 0 removals + - Locking asm89/stack-cors (v2.3.0) + - Locking chi-teck/drupal-code-generator (4.2.0) + - Locking codemirror/codemirror (5.65.12) + - Locking composer/ca-bundle (1.5.9) + - Locking composer/class-map-generator (1.7.0) + - Locking composer/composer (2.9.2) + - Locking composer/installers (v2.3.0) + - Locking composer/metadata-minifier (1.0.0) + - Locking composer/pcre (3.3.2) + - Locking composer/semver (3.4.4) + - Locking composer/spdx-licenses (1.5.9) + - Locking composer/xdebug-handler (3.0.5) + - Locking consolidation/annotated-command (4.10.4) + - Locking consolidation/config (3.2.0) + - Locking consolidation/filter-via-dot-access-data (2.0.3) + - Locking consolidation/log (3.1.1) + - Locking consolidation/output-formatters (4.7.0) + - Locking consolidation/robo (5.1.1) + - Locking consolidation/site-alias (4.1.2) + - Locking consolidation/site-process (5.4.2) + - Locking cweagans/composer-configurable-plugin (2.0.0) + - Locking cweagans/composer-patches (2.0.0) + - Locking dflydev/dot-access-data (v3.0.3) + - Locking doctrine/annotations (2.0.2) + - Locking doctrine/deprecations (1.1.5) + - Locking doctrine/lexer (2.1.1) + - Locking dragonmantank/cron-expression (v3.6.0) + - Locking drupal/ai (1.2.4) + - Locking drupal/ai_agents (1.2.1) + - Locking drupal/ai_image_alt_text (1.0.1) + - Locking drupal/ai_provider_anthropic (1.2.0) + - Locking drupal/ai_provider_openai (1.2.0) + - Locking drupal/automatic_updates (4.0.0) + - Locking drupal/autosave_form (1.10.0) + - Locking drupal/better_exposed_filters (7.1.1) + - Locking drupal/bpmn_io (3.0.2) + - Locking drupal/byte (1.0.0-alpha5) + - Locking drupal/canvas (1.0.0) + - Locking drupal/captcha (2.0.9) + - Locking drupal/checklistapi (2.1.7) + - Locking drupal/coffee (2.0.1) + - Locking drupal/core (11.2.9) + - Locking drupal/core-composer-scaffold (11.2.9) + - Locking drupal/core-project-message (11.2.9) + - Locking drupal/core-recipe-unpack (11.2.9) + - Locking drupal/core-recommended (11.2.9) + - Locking drupal/core-vendor-hardening (11.2.9) + - Locking drupal/crop (2.5.0) + - Locking drupal/ctools (4.1.0) + - Locking drupal/cva (1.0.0-beta1) + - Locking drupal/dashboard (2.1.1) + - Locking drupal/drupal_cms_accessibility_tools (2.0.0-alpha2) + - Locking drupal/drupal_cms_admin_ui (2.0.0-alpha2) + - Locking drupal/drupal_cms_ai (2.0.0-alpha2) + - Locking drupal/drupal_cms_anti_spam (2.0.0-alpha2) + - Locking drupal/drupal_cms_authentication (2.0.0-alpha2) + - Locking drupal/drupal_cms_blog (2.0.0-alpha2) + - Locking drupal/drupal_cms_content_type_base (2.0.0-alpha2) + - Locking drupal/drupal_cms_forms (2.0.0-alpha2) + - Locking drupal/drupal_cms_google_analytics (2.0.0-alpha2) + - Locking drupal/drupal_cms_helper (2.0.0-alpha2) + - Locking drupal/drupal_cms_image (2.0.0-alpha2) + - Locking drupal/drupal_cms_page (2.0.0-alpha2) + - Locking drupal/drupal_cms_privacy_basic (2.0.0-alpha2) + - Locking drupal/drupal_cms_remote_video (2.0.0-alpha2) + - Locking drupal/drupal_cms_search (2.0.0-alpha2) + - Locking drupal/drupal_cms_seo_basic (2.0.0-alpha2) + - Locking drupal/drupal_cms_seo_tools (2.0.0-alpha2) + - Locking drupal/drupal_cms_starter (2.0.0-alpha2) + - Locking drupal/easy_breadcrumb (2.0.9) + - Locking drupal/easy_email (3.0.7) + - Locking drupal/easy_email_express (1.0.4) + - Locking drupal/easy_email_standard (1.0.3) + - Locking drupal/easy_email_text_format (1.0.3) + - Locking drupal/easy_email_theme (1.1.0) + - Locking drupal/easy_email_types_core (1.0.4) + - Locking drupal/easy_email_types_default (1.0.2) + - Locking drupal/eca (3.0.8) + - Locking drupal/editoria11y (2.2.18) + - Locking drupal/field_group (4.0.0) + - Locking drupal/focal_point (2.1.2) + - Locking drupal/friendly_captcha_challenge (0.9.18) + - Locking drupal/friendlycaptcha (1.1.4) + - Locking drupal/gin (5.0.9) + - Locking drupal/gin_login (2.1.3) + - Locking drupal/gin_toolbar (3.0.2) + - Locking drupal/google_tag (2.0.9) + - Locking drupal/honeypot (2.2.2) + - Locking drupal/jquery_ui (1.8.0) + - Locking drupal/jquery_ui_resizable (2.1.0) + - Locking drupal/key (1.20.0) + - Locking drupal/klaro (3.0.7) + - Locking drupal/klaro_js (3.0.1) + - Locking drupal/linkit (7.0.11) + - Locking drupal/login_emailusername (3.0.1) + - Locking drupal/mailsystem (4.5.0) + - Locking drupal/menu_link_attributes (1.5.0) + - Locking drupal/mercury (1.0.0-alpha2) + - Locking drupal/metatag (2.2.0) + - Locking drupal/modeler_api (1.0.6) + - Locking drupal/nouislider_js (15.8.1) + - Locking drupal/pathauto (1.14.0) + - Locking drupal/project_browser (2.1.2) + - Locking drupal/recipe_installer_kit (1.0.0) + - Locking drupal/redirect (1.12.0) + - Locking drupal/robotstxt (1.6.0) + - Locking drupal/sam (1.3.2) + - Locking drupal/scheduler (2.2.2) + - Locking drupal/scheduler_content_moderation_integration (3.0.4) + - Locking drupal/search_api (1.40.0) + - Locking drupal/search_api_autocomplete (1.11.0) + - Locking drupal/search_api_exclude (2.0.3) + - Locking drupal/selective_better_exposed_filters (3.0.3) + - Locking drupal/seo_checklist (5.2.4) + - Locking drupal/simple_search_form (1.8.0) + - Locking drupal/simple_sitemap (4.2.3) + - Locking drupal/sitemap (2.5.0) + - Locking drupal/svg_image (3.2.2) + - Locking drupal/symfony_mailer_lite (2.0.4) + - Locking drupal/tagify (1.2.44) + - Locking drupal/token (1.16.0) + - Locking drupal/token_or (2.3.2) + - Locking drupal/trash (3.0.22) + - Locking drupal/ui_icons (1.1.0-beta6) + - Locking drupal/webform (6.3.0-beta6) + - Locking drupal/yoast_seo (2.2.0) + - Locking drush/drush (13.7.0) + - Locking egulias/email-validator (4.0.4) + - Locking enshrined/svg-sanitize (0.22.0) + - Locking goalgorilla/rtseo.js (2.1.0) + - Locking grasmash/expander (3.0.1) + - Locking grasmash/yaml-cli (3.2.1) + - Locking guzzlehttp/guzzle (7.9.3) + - Locking guzzlehttp/promises (2.2.0) + - Locking guzzlehttp/psr7 (2.7.1) + - Locking html2text/html2text (4.3.2) + - Locking jquery/inputmask (5.0.9) + - Locking jquery/intl-tel-input (17.0.19) + - Locking jquery/rateit (1.1.5) + - Locking jquery/select2 (4.0.13) + - Locking jquery/textcounter (0.9.1) + - Locking jquery/timepicker (1.14.0) + - Locking justinrainbow/json-schema (6.6.3) + - Locking laravel/prompts (v0.3.8) + - Locking league/commonmark (2.8.0) + - Locking league/config (v1.2.0) + - Locking league/container (4.2.5) + - Locking league/html-to-markdown (5.1.1) + - Locking marc-mabe/php-enum (v4.7.2) + - Locking masterminds/html5 (2.9.0) + - Locking mck89/peast (v1.17.4) + - Locking mtownsend/xml-to-array (2.0.0) + - Locking nette/schema (v1.3.3) + - Locking nette/utils (v4.1.0) + - Locking nikic/php-parser (v5.6.2) + - Locking openai-php/client (v0.18.0) + - Locking pear/archive_tar (1.5.0) + - Locking pear/console_getopt (v1.4.3) + - Locking pear/pear-core-minimal (v1.10.16) + - Locking pear/pear_exception (v1.0.2) + - Locking phootwork/collection (v3.2.3) + - Locking phootwork/lang (v3.2.3) + - Locking php-http/discovery (1.20.0) + - Locking php-http/multipart-stream-builder (1.4.2) + - Locking php-tuf/composer-stager (v2.0.2) + - Locking phpowermove/docblock (v4.0) + - Locking popperjs/popperjs (2.11.6) + - Locking progress-tracker/progress-tracker (2.0.7) + - Locking psr/cache (3.0.0) + - Locking psr/container (2.0.2) + - Locking psr/event-dispatcher (1.0.0) + - Locking psr/http-client (1.0.3) + - Locking psr/http-factory (1.1.0) + - Locking psr/http-message (2.0) + - Locking psr/log (3.0.2) + - Locking psy/psysh (v0.12.15) + - Locking ralouphie/getallheaders (3.0.3) + - Locking react/promise (v3.3.0) + - Locking revolt/event-loop (v1.0.8) + - Locking sebastian/diff (7.0.0) + - Locking seld/jsonlint (1.11.0) + - Locking seld/phar-utils (1.2.1) + - Locking seld/signal-handler (2.0.2) + - Locking signature_pad/signature_pad (2.3.0) + - Locking symfony/console (v7.3.6) + - Locking symfony/css-selector (v7.4.0) + - Locking symfony/dependency-injection (v7.3.6) + - Locking symfony/deprecation-contracts (v3.6.0) + - Locking symfony/error-handler (v7.3.6) + - Locking symfony/event-dispatcher (v7.3.3) + - Locking symfony/event-dispatcher-contracts (v3.6.0) + - Locking symfony/filesystem (v7.3.6) + - Locking symfony/finder (v7.3.5) + - Locking symfony/http-foundation (v7.3.7) + - Locking symfony/http-kernel (v7.3.7) + - Locking symfony/mailer (v7.3.5) + - Locking symfony/mime (v7.3.4) + - Locking symfony/polyfill-ctype (v1.32.0) + - Locking symfony/polyfill-iconv (v1.32.0) + - Locking symfony/polyfill-intl-grapheme (v1.32.0) + - Locking symfony/polyfill-intl-idn (v1.32.0) + - Locking symfony/polyfill-intl-normalizer (v1.32.0) + - Locking symfony/polyfill-mbstring (v1.32.0) + - Locking symfony/polyfill-php73 (v1.33.0) + - Locking symfony/polyfill-php80 (v1.33.0) + - Locking symfony/polyfill-php81 (v1.33.0) + - Locking symfony/polyfill-php83 (v1.33.0) + - Locking symfony/polyfill-php84 (v1.32.0) + - Locking symfony/process (v7.3.4) + - Locking symfony/psr-http-message-bridge (v7.3.0) + - Locking symfony/routing (v7.3.6) + - Locking symfony/serializer (v7.3.5) + - Locking symfony/service-contracts (v3.6.1) + - Locking symfony/string (v7.3.4) + - Locking symfony/translation-contracts (v3.6.1) + - Locking symfony/validator (v7.3.7) + - Locking symfony/var-dumper (v7.3.5) + - Locking symfony/var-exporter (v7.3.4) + - Locking symfony/yaml (v7.3.5) + - Locking tabby/tabby (12.0.3) + - Locking tijsverkoyen/css-to-inline-styles (v2.3.0) + - Locking tippyjs/tippyjs (6.3.7) + - Locking twig/html-extra (v3.22.1) + - Locking twig/twig (v3.21.1) + - Locking yethee/tiktoken (0.5.1) +Writing lock file +Installing dependencies from lock file +Package operations: 225 installs, 0 updates, 0 removals + - Installing cweagans/composer-configurable-plugin (2.0.0): Extracting archive + - Installing cweagans/composer-patches (2.0.0): Extracting archive +patches.lock.json does not exist. Creating a new patches.lock.json. + - Resolving patches from dependencies. + - Installing composer/installers (v2.3.0): Extracting archive + - Installing drupal/core-composer-scaffold (11.2.9): Extracting archive + - Installing drupal/core-project-message (11.2.9): Extracting archive + - Installing drupal/core-recipe-unpack (11.2.9): Extracting archive + - Installing drupal/core-vendor-hardening (11.2.9): Extracting archive + - Installing php-http/discovery (1.20.0): Extracting archive + - Installing codemirror/codemirror (5.65.12): Extracting archive + - Installing symfony/process (v7.3.4): Extracting archive + - Installing symfony/polyfill-php84 (v1.32.0): Extracting archive + - Installing symfony/polyfill-php81 (v1.33.0): Extracting archive + - Installing symfony/polyfill-php80 (v1.33.0): Extracting archive + - Installing symfony/polyfill-php73 (v1.33.0): Extracting archive + - Installing symfony/finder (v7.3.5): Extracting archive + - Installing symfony/polyfill-iconv (v1.32.0): Extracting archive + - Installing symfony/polyfill-mbstring (v1.32.0): Extracting archive + - Installing symfony/polyfill-ctype (v1.32.0): Extracting archive + - Installing symfony/filesystem (v7.3.6): Extracting archive + - Installing symfony/polyfill-intl-normalizer (v1.32.0): Extracting archive + - Installing symfony/polyfill-intl-grapheme (v1.32.0): Extracting archive + - Installing symfony/string (v7.3.4): Extracting archive + - Installing symfony/deprecation-contracts (v3.6.0): Extracting archive + - Installing psr/container (2.0.2): Extracting archive + - Installing symfony/service-contracts (v3.6.1): Extracting archive + - Installing symfony/console (v7.3.6): Extracting archive + - Installing seld/signal-handler (2.0.2): Extracting archive + - Installing seld/phar-utils (1.2.1): Extracting archive + - Installing seld/jsonlint (1.11.0): Extracting archive + - Installing react/promise (v3.3.0): Extracting archive + - Installing psr/log (3.0.2): Extracting archive + - Installing marc-mabe/php-enum (v4.7.2): Extracting archive + - Installing justinrainbow/json-schema (6.6.3): Extracting archive + - Installing composer/pcre (3.3.2): Extracting archive + - Installing composer/xdebug-handler (3.0.5): Extracting archive + - Installing composer/spdx-licenses (1.5.9): Extracting archive + - Installing composer/semver (3.4.4): Extracting archive + - Installing composer/metadata-minifier (1.0.0): Extracting archive + - Installing composer/class-map-generator (1.7.0): Extracting archive + - Installing composer/ca-bundle (1.5.9): Extracting archive + - Installing composer/composer (2.9.2): Extracting archive + - Installing consolidation/log (3.1.1): Extracting archive + - Installing twig/twig (v3.21.1): Extracting archive + - Installing symfony/yaml (v7.3.5): Extracting archive + - Installing symfony/translation-contracts (v3.6.1): Extracting archive + - Installing symfony/polyfill-php83 (v1.33.0): Extracting archive + - Installing symfony/validator (v7.3.7): Extracting archive + - Installing symfony/serializer (v7.3.5): Extracting archive + - Installing symfony/routing (v7.3.6): Extracting archive + - Installing symfony/http-foundation (v7.3.7): Extracting archive + - Installing psr/http-message (2.0): Extracting archive + - Installing symfony/psr-http-message-bridge (v7.3.0): Extracting archive + - Installing symfony/polyfill-intl-idn (v1.32.0): Extracting archive + - Installing symfony/mime (v7.3.4): Extracting archive + - Installing psr/event-dispatcher (1.0.0): Extracting archive + - Installing symfony/event-dispatcher-contracts (v3.6.0): Extracting archive + - Installing symfony/event-dispatcher (v7.3.3): Extracting archive + - Installing doctrine/deprecations (1.1.5): Extracting archive + - Installing doctrine/lexer (2.1.1): Extracting archive + - Installing egulias/email-validator (4.0.4): Extracting archive + - Installing symfony/mailer (v7.3.5): Extracting archive + - Installing symfony/var-dumper (v7.3.5): Extracting archive + - Installing symfony/error-handler (v7.3.6): Extracting archive + - Installing symfony/http-kernel (v7.3.7): Extracting archive + - Installing symfony/var-exporter (v7.3.4): Extracting archive + - Installing symfony/dependency-injection (v7.3.6): Extracting archive + - Installing sebastian/diff (7.0.0): Extracting archive + - Installing revolt/event-loop (v1.0.8): Extracting archive + - Installing php-tuf/composer-stager (v2.0.2): Extracting archive + - Installing pear/pear_exception (v1.0.2): Extracting archive + - Installing pear/console_getopt (v1.4.3): Extracting archive + - Installing pear/pear-core-minimal (v1.10.16): Extracting archive + - Installing pear/archive_tar (1.5.0): Extracting archive + - Installing mck89/peast (v1.17.4): Extracting archive + - Installing masterminds/html5 (2.9.0): Extracting archive + - Installing ralouphie/getallheaders (3.0.3): Extracting archive + - Installing psr/http-factory (1.1.0): Extracting archive + - Installing guzzlehttp/psr7 (2.7.1): Extracting archive + - Installing psr/http-client (1.0.3): Extracting archive + - Installing guzzlehttp/promises (2.2.0): Extracting archive + - Installing guzzlehttp/guzzle (7.9.3): Extracting archive + - Installing psr/cache (3.0.0): Extracting archive + - Installing doctrine/annotations (2.0.2): Extracting archive + - Installing asm89/stack-cors (v2.3.0): Extracting archive + - Installing drupal/core (11.2.9): Extracting archive + - Installing drupal/ui_icons (1.1.0-beta6): Extracting archive + - Installing drupal/mercury (1.0.0-alpha2): Extracting archive + - Installing drupal/easy_email_text_format (1.0.3): Extracting archive + - Installing drupal/token (1.16.0): Extracting archive + - Installing drupal/jquery_ui (1.8.0): Extracting archive + - Installing drupal/jquery_ui_resizable (2.1.0): Extracting archive + - Installing drupal/easy_email (3.0.7): Extracting archive + - Installing drupal/easy_email_types_default (1.0.2): Extracting archive + - Installing drupal/easy_email_types_core (1.0.4): Extracting archive + - Installing symfony/css-selector (v7.4.0): Extracting archive + - Installing tijsverkoyen/css-to-inline-styles (v2.3.0): Extracting archive + - Installing html2text/html2text (4.3.2): Extracting archive + - Installing drupal/mailsystem (4.5.0): Extracting archive + - Installing drupal/symfony_mailer_lite (2.0.4): Extracting archive + - Installing drupal/easy_email_theme (1.1.0): Extracting archive + - Installing drupal/easy_email_standard (1.0.3): Extracting archive + - Installing drupal/easy_email_express (1.0.4): Extracting archive + - Installing goalgorilla/rtseo.js (2.1.0): Extracting archive + - Installing drupal/metatag (2.2.0): Extracting archive + - Installing drupal/yoast_seo (2.2.0): Extracting archive + - Installing drupal/token_or (2.3.2): Extracting archive + - Installing drupal/sitemap (2.5.0): Extracting archive + - Installing drupal/simple_sitemap (4.2.3): Extracting archive + - Installing drupal/checklistapi (2.1.7): Extracting archive + - Installing drupal/seo_checklist (5.2.4): Extracting archive + - Installing drupal/robotstxt (1.6.0): Extracting archive + - Installing drupal/modeler_api (1.0.6): Extracting archive + - Installing drupal/crop (2.5.0): Extracting archive + - Installing drupal/focal_point (2.1.2): Extracting archive + - Installing drupal/field_group (4.0.0): Extracting archive + - Installing dragonmantank/cron-expression (v3.6.0): Extracting archive + - Installing drupal/eca (3.0.8): Extracting archive + - Installing mtownsend/xml-to-array (2.0.0): Extracting archive + - Installing drupal/bpmn_io (3.0.2): Extracting archive + - Installing drupal/drupal_cms_seo_tools (2.0.0-alpha2): Extracting archive + - Installing drupal/menu_link_attributes (1.5.0): Extracting archive + - Installing drupal/klaro_js (3.0.1): Extracting archive + - Installing drupal/klaro (3.0.7): Extracting archive + - Installing drupal/trash (3.0.22): Extracting archive + - Installing drupal/tagify (1.2.44): Extracting archive + - Installing drupal/scheduler (2.2.2): Extracting archive + - Installing drupal/scheduler_content_moderation_integration (3.0.4): Extracting archive + - Installing drupal/ctools (4.1.0): Extracting archive + - Installing drupal/pathauto (1.14.0): Extracting archive + - Installing drupal/linkit (7.0.11): Extracting archive + - Installing enshrined/svg-sanitize (0.22.0): Extracting archive + - Installing drupal/svg_image (3.2.2): Extracting archive + - Installing drupal/drupal_cms_image (2.0.0-alpha2): Extracting archive + - Installing drupal/drupal_cms_helper (2.0.0-alpha2): Extracting archive + - Installing drupal/canvas (1.0.0): Extracting archive + - Installing drupal/autosave_form (1.10.0): Extracting archive + - Installing drupal/drupal_cms_content_type_base (2.0.0-alpha2): Extracting archive + - Installing drupal/drupal_cms_page (2.0.0-alpha2): Extracting archive + - Installing drupal/drupal_cms_privacy_basic (2.0.0-alpha2): Extracting archive + - Installing drupal/drupal_cms_remote_video (2.0.0-alpha2): Extracting archive + - Installing drupal/webform (6.3.0-beta6): Extracting archive + - Installing drupal/honeypot (2.2.2): Extracting archive + - Installing drupal/captcha (2.0.9): Extracting archive + - Installing drupal/friendlycaptcha (1.1.4): Extracting archive + - Installing drupal/friendly_captcha_challenge (0.9.18): Extracting archive + - Installing drupal/drupal_cms_anti_spam (2.0.0-alpha2): Extracting archive + - Installing drupal/drupal_cms_forms (2.0.0-alpha2): Extracting archive + - Installing drupal/nouislider_js (15.8.1): Extracting archive + - Installing drupal/better_exposed_filters (7.1.1): Extracting archive + - Installing drupal/selective_better_exposed_filters (3.0.3): Extracting archive + - Installing drupal/drupal_cms_blog (2.0.0-alpha2): Extracting archive + - Installing drupal/login_emailusername (3.0.1): Extracting archive + - Installing drupal/drupal_cms_authentication (2.0.0-alpha2): Extracting archive + - Installing drupal/sam (1.3.2): Extracting archive + - Installing drupal/project_browser (2.1.2): Extracting archive + - Installing drupal/gin_login (2.1.3): Extracting archive + - Installing drupal/gin_toolbar (3.0.2): Extracting archive + - Installing drupal/gin (5.0.9): Extracting archive + - Installing drupal/dashboard (2.1.1): Extracting archive + - Installing drupal/coffee (2.0.1): Extracting archive + - Installing drupal/drupal_cms_admin_ui (2.0.0-alpha2): Extracting archive + - Installing drupal/byte (1.0.0-alpha5): Extracting archive + - Installing drupal/core-recommended (11.2.9) + - Installing twig/html-extra (v3.22.1): Extracting archive + - Installing drupal/cva (1.0.0-beta1): Extracting archive + - Installing drupal/editoria11y (2.2.18): Extracting archive + - Installing drupal/drupal_cms_accessibility_tools (2.0.0-alpha2): Extracting archive + - Installing nette/utils (v4.1.0): Extracting archive + - Installing nette/schema (v1.3.3): Extracting archive + - Installing dflydev/dot-access-data (v3.0.3): Extracting archive + - Installing league/config (v1.2.0): Extracting archive + - Installing league/commonmark (2.8.0): Extracting archive + - Installing drupal/key (1.20.0): Extracting archive + - Installing yethee/tiktoken (0.5.1): Extracting archive + - Installing php-http/multipart-stream-builder (1.4.2): Extracting archive + - Installing openai-php/client (v0.18.0): Extracting archive + - Installing league/html-to-markdown (5.1.1): Extracting archive + - Installing drupal/ai (1.2.4): Extracting archive + - Installing drupal/ai_provider_openai (1.2.0): Extracting archive + - Installing drupal/ai_provider_anthropic (1.2.0): Extracting archive + - Installing drupal/ai_image_alt_text (1.0.1): Extracting archive + - Installing drupal/ai_agents (1.2.1): Extracting archive + - Installing drupal/drupal_cms_ai (2.0.0-alpha2): Extracting archive + - Installing drupal/google_tag (2.0.9): Extracting archive + - Installing drupal/drupal_cms_google_analytics (2.0.0-alpha2): Extracting archive + - Installing drupal/redirect (1.12.0): Extracting archive + - Installing drupal/easy_breadcrumb (2.0.9): Extracting archive + - Installing drupal/drupal_cms_seo_basic (2.0.0-alpha2): Extracting archive + - Installing drupal/simple_search_form (1.8.0): Extracting archive + - Installing drupal/search_api (1.40.0): Extracting archive + - Installing drupal/search_api_exclude (2.0.3): Extracting archive + - Installing drupal/search_api_autocomplete (1.11.0): Extracting archive + - Installing drupal/drupal_cms_search (2.0.0-alpha2): Extracting archive + - Installing drupal/automatic_updates (4.0.0): Extracting archive + - Installing drupal/drupal_cms_starter (2.0.0-alpha2): Extracting archive + - Installing nikic/php-parser (v5.6.2): Extracting archive + - Installing psy/psysh (v0.12.15): Extracting archive + - Installing league/container (4.2.5): Extracting archive + - Installing laravel/prompts (v0.3.8): Extracting archive + - Installing grasmash/yaml-cli (3.2.1): Extracting archive + - Installing grasmash/expander (3.0.1): Extracting archive + - Installing consolidation/config (3.2.0): Extracting archive + - Installing consolidation/site-alias (4.1.2): Extracting archive + - Installing consolidation/site-process (5.4.2): Extracting archive + - Installing phootwork/lang (v3.2.3): Extracting archive + - Installing phootwork/collection (v3.2.3): Extracting archive + - Installing phpowermove/docblock (v4.0): Extracting archive + - Installing consolidation/output-formatters (4.7.0): Extracting archive + - Installing consolidation/annotated-command (4.10.4): Extracting archive + - Installing consolidation/robo (5.1.1): Extracting archive + - Installing consolidation/filter-via-dot-access-data (2.0.3): Extracting archive + - Installing chi-teck/drupal-code-generator (4.2.0): Extracting archive + - Installing drush/drush (13.7.0): Extracting archive + - Installing drupal/recipe_installer_kit (1.0.0): Extracting archive + - Installing jquery/inputmask (5.0.9): Extracting archive + - Installing jquery/intl-tel-input (17.0.19): Extracting archive + - Installing jquery/rateit (1.1.5): Extracting archive + - Installing jquery/select2 (4.0.13): Extracting archive + - Installing jquery/textcounter (0.9.1): Extracting archive + - Installing jquery/timepicker (1.14.0): Extracting archive + - Installing popperjs/popperjs (2.11.6): Extracting archive + - Installing progress-tracker/progress-tracker (2.0.7): Extracting archive + - Installing signature_pad/signature_pad (2.3.0): Extracting archive + - Installing tabby/tabby (12.0.3): Extracting archive + - Installing tippyjs/tippyjs (6.3.7): Extracting archive + Cleaning: symfony/process + Cleaning: symfony/finder + Cleaning: symfony/filesystem + Cleaning: symfony/console + Cleaning: seld/jsonlint + Cleaning: justinrainbow/json-schema + Cleaning: twig/twig + Cleaning: symfony/yaml + Cleaning: symfony/validator + Cleaning: symfony/serializer + Cleaning: symfony/routing + Cleaning: symfony/http-foundation + Cleaning: symfony/psr-http-message-bridge + Cleaning: symfony/event-dispatcher + Cleaning: egulias/email-validator + Cleaning: symfony/var-dumper + Cleaning: symfony/http-kernel + Cleaning: symfony/dependency-injection + Cleaning: sebastian/diff + Cleaning: pear/pear_exception + Cleaning: pear/console_getopt + Cleaning: pear/pear-core-minimal + Cleaning: pear/archive_tar + Cleaning: mck89/peast + Cleaning: masterminds/html5 + Cleaning: guzzlehttp/psr7 + Cleaning: guzzlehttp/promises + Cleaning: symfony/css-selector + - Patching drupal/canvas + - Found cached patch at /mnt/ddev-global-cache/composer/patches/dd1ee073adaa9b96b834661b2aa6781620cbbf87ae95be5f58bc2e568b92f9bd.patch + +In Patches.php line 288: + + No available patcher was able to apply patch https://git.drupalcode.org/project/canvas/-/merge_requests/187.diff to dr + upal/canvas + + +update [--with WITH] [--prefer-source] [--prefer-dist] [--prefer-install PREFER-INSTALL] [--dry-run] [--dev] [--no-dev] [--lock] [--no-install] [--no-audit] [--audit-format AUDIT-FORMAT] [--no-security-blocking] [--no-autoloader] [--no-suggest] [--no-progress] [-w|--with-dependencies] [-W|--with-all-dependencies] [-v|vv|vvv|--verbose] [-o|--optimize-autoloader] [-a|--classmap-authoritative] [--apcu-autoloader] [--apcu-autoloader-prefix APCU-AUTOLOADER-PREFIX] [--ignore-platform-req IGNORE-PLATFORM-REQ] [--ignore-platform-reqs] [--prefer-stable] [--prefer-lowest] [-m|--minimal-changes] [--patch-only] [-i|--interactive] [--root-reqs] [--bump-after-update [BUMP-AFTER-UPDATE]] [--] [...] + +0m12.372s +Composer update encountered errors (likely patch failures), but continuing... +Regenerating autoload files... +Generating optimized autoload files +Hardening vendor directory with .htaccess file. +Generated optimized autoload files containing 7393 classes +All modules installed and ready! + +Create the private files directory. +0m0.002s + +Create the config sync directory. +0m0.003s + +Install Drupal. +drush is not available. You may need to 'ddev composer require drush/drush' +0m0.047s diff --git a/.devpanel/pgvector_installer.sh b/.devpanel/pgvector_installer.sh new file mode 100644 index 00000000..9dab4a36 --- /dev/null +++ b/.devpanel/pgvector_installer.sh @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +#Update sudo (might be overkill). +time sudo apt-get update + +# Prepare so it works in devpanel also. +sudo apt -y install curl ca-certificates apt-transport-https +sudo install -d /usr/share/postgresql-common/pgdg +sudo curl -o /usr/share/postgresql-common/pgdg/apt.postgresql.org.asc --fail https://www.postgresql.org/media/keys/ACCC4CF8.asc +. /etc/os-release +sudo sh -c "echo 'deb [signed-by=/usr/share/postgresql-common/pgdg/apt.postgresql.org.asc] https://apt.postgresql.org/pub/repos/apt $VERSION_CODENAME-pgdg main' > /etc/apt/sources.list.d/pgdg.list" +sudo apt-get update + +#== Install postgresql on the host. +echo 'PostgreSQL is not installed. Installing it now.' +time sudo apt-get install -y postgresql postgresql-17-pgvector postgresql-client +#== Make it less promiscuous in DDEV only. +if env | grep -q DDEV_PROJECT; then + sudo chmod 0755 /usr/bin + sudo chmod 0755 /usr/sbin + #== Start the PostgreSQL service. + env PATH="/usr/sbin:/usr/bin:/sbin:/bin" sudo service postgresql start +else + #== In Devpanel we always install fresh. So we skip the check. + #== Start the postgresql service. + sudo service postgresql start + #== Create the user. + sudo su postgres -c "psql -c \"CREATE ROLE db WITH LOGIN PASSWORD 'db';\"" + #== Create the database. + sudo su postgres -c "psql -c \"CREATE DATABASE db WITH OWNER db ENCODING 'UTF8' LC_COLLATE='C' LC_CTYPE='C' TEMPLATE template0;\"" + #== Enable pgvector extension. + sudo su postgres -c "psql -d db -c \"CREATE EXTENSION IF NOT EXISTS vector;\"" +fi + +# Make sure that php has pgsql installed. +sudo apt install -y libpq-dev +sudo -E docker-php-ext-install pgsql diff --git a/.devpanel/re-config.sh b/.devpanel/re-config.sh index c7a9f0b3..6c79bcb8 100755 --- a/.devpanel/re-config.sh +++ b/.devpanel/re-config.sh @@ -15,10 +15,20 @@ # For GNU Affero General Public License see . # ---------------------------------------------------------------------- -#== If webRoot has not been difined, we will set appRoot to webRoot -if [[ ! -n "$WEB_ROOT" ]]; then - export WEB_ROOT=$APP_ROOT -fi +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Directory Setup (works in both DDEV and GitHub Actions environments) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Find the .devpanel directory (where this script lives) +DEVPANEL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Project root is one level up from .devpanel +PROJECT_ROOT="$(dirname "$DEVPANEL_DIR")" +# APP_ROOT is the composer root (from environment or default to PROJECT_ROOT) +APP_ROOT="${APP_ROOT:-$PROJECT_ROOT}" +# WEB_ROOT from environment (with fallback) +WEB_ROOT="${WEB_ROOT:-$APP_ROOT/web}" + +# Define drush command +DRUSH="$APP_ROOT/vendor/bin/drush" STATIC_FILES_PATH="$WEB_ROOT/sites/default/files/" SETTINGS_FILES_PATH="$WEB_ROOT/sites/default/settings.php" @@ -56,6 +66,6 @@ if [[ $(mysql -h$DB_HOST -P$DB_PORT -u$DB_USER -p$DB_PASSWORD $DB_NAME -e "show #== Import mysql files if [[ -f "$APP_ROOT/.devpanel/dumps/db.sql.gz" ]]; then echo 'Import mysql file ...' - drush sqlq --file="$APP_ROOT/.devpanel/dumps/db.sql.gz" --file-delete + "$DRUSH" sqlq --file="$APP_ROOT/.devpanel/dumps/db.sql.gz" --file-delete fi fi diff --git a/.devpanel/repos/.gitkeep b/.devpanel/repos/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/.devpanel/salt.txt b/.devpanel/salt.txt new file mode 100644 index 00000000..5860313c --- /dev/null +++ b/.devpanel/salt.txt @@ -0,0 +1 @@ +d529034bdfcacb2f9d15f178c24ac11c9dcdb1838a94f203b471de3318c07e29 diff --git a/.devpanel/setup_ai.sh b/.devpanel/setup_ai.sh new file mode 100755 index 00000000..1d519791 --- /dev/null +++ b/.devpanel/setup_ai.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +set -eu -o pipefail + + +echo +time $DRUSH -n en ai_provider_litellm +$DRUSH -n key-save litellm_api_key --label="LiteLLM API key" --key-provider=env --key-provider-settings='{ + "env_variable": "DP_AI_VIRTUAL_KEY", + "base64_encoded": false, + "strip_line_breaks": true +}' +$DRUSH -n cset ai_provider_litellm.settings api_key litellm_api_key +$DRUSH -n cset ai_provider_litellm.settings moderation false --input-format yaml +$DRUSH -n cset ai_provider_litellm.settings host "${DP_AI_HOST:="https://ai.drupalforge.org"}" +$DRUSH -q recipe ../recipes/drupal_cms_ai --input=drupal_cms_ai.provider=litellm +$DRUSH -n cset ai.settings default_providers.chat.provider_id litellm +$DRUSH -n cset ai.settings default_providers.chat.model_id openai/gpt-4o-mini +$DRUSH -n cset ai.settings default_providers.chat_with_complex_json.provider_id litellm +$DRUSH -n cset ai.settings default_providers.chat_with_complex_json.model_id openai/gpt-4o-mini +$DRUSH -n cset ai.settings default_providers.chat_with_image_vision.provider_id litellm +$DRUSH -n cset ai.settings default_providers.chat_with_image_vision.model_id openai/gpt-4o-mini +$DRUSH -n cset ai.settings default_providers.chat_with_structured_response.provider_id litellm +$DRUSH -n cset ai.settings default_providers.chat_with_structured_response.model_id openai/gpt-4o-mini +$DRUSH -n cset ai.settings default_providers.chat_with_tools.provider_id litellm +$DRUSH -n cset ai.settings default_providers.chat_with_tools.model_id openai/gpt-4o-mini +$DRUSH -n cset ai.settings default_providers.embeddings.provider_id litellm +$DRUSH -n cset ai.settings default_providers.embeddings.model_id openai/text-embedding-3-small +$DRUSH -n cset ai.settings default_providers.text_to_speech.provider_id litellm +$DRUSH -n cset ai.settings default_providers.text_to_speech.model_id openai/gpt-4o-mini-realtime-preview +$DRUSH -n cset ai_assistant_api.ai_assistant.drupal_cms_assistant llm_provider __default__ +$DRUSH -n cset klaro.klaro_app.deepchat status 0 \ No newline at end of file diff --git a/.github/workflows/docker-publish-image.yml b/.github/workflows/docker-publish-image.yml index fb516022..0ecb68f3 100644 --- a/.github/workflows/docker-publish-image.yml +++ b/.github/workflows/docker-publish-image.yml @@ -5,9 +5,16 @@ on: drupal_core_version: required: false type: string + description: 'Drupal version to install (e.g., 1.x, 11.x, 11.2.5)' webserver_image: required: true type: string + description: 'Base PHP image (e.g., devpanel/php:8.3-base-ai)' + image_tag_suffix: + required: false + type: string + default: '' + description: 'Tag suffix (cms or core) - auto-detects starter template' secrets: DOCKERHUB_TOKEN: required: true @@ -33,7 +40,8 @@ jobs: DB_USER: user DB_PASSWORD: password DB_DRIVER: mysql - DP_CORE_VERSION: ${{ inputs.drupal_core_version || '' }} + DP_VERSION: ${{ inputs.drupal_core_version || '' }} + DP_IMAGE: drupalforge/drupalpod-ai-qa:${{ inputs.image_tag_suffix || 'latest' }} ports: - 80:80 volumes: @@ -86,20 +94,36 @@ jobs: - name: Set env to PHP 8.2 environment if: endsWith(inputs.webserver_image, ':8.2-base-ai') run: | - echo "IMAGE_TAG=drupalforge/drupalpod:php-8.2" >> $GITHUB_ENV + SUFFIX="${{ inputs.image_tag_suffix }}" + if [ -n "$SUFFIX" ]; then + echo "IMAGE_TAG=drupalforge/drupalpod-ai-qa:php-8.2-${SUFFIX}" >> $GITHUB_ENV + else + echo "IMAGE_TAG=drupalforge/drupalpod-ai-qa:php-8.2" >> $GITHUB_ENV + fi - name: Set env to PHP 8.3 environment if: endsWith(inputs.webserver_image, ':8.3-base-ai') run: | - echo "IMAGE_TAG=drupalforge/drupalpod:php-8.3" >> $GITHUB_ENV + SUFFIX="${{ inputs.image_tag_suffix }}" + if [ -n "$SUFFIX" ]; then + echo "IMAGE_TAG=drupalforge/drupalpod-ai-qa:php-8.3-${SUFFIX}" >> $GITHUB_ENV + else + echo "IMAGE_TAG=drupalforge/drupalpod-ai-qa:php-8.3" >> $GITHUB_ENV + fi - name: Commit and push the container state to Docker Hub run: | docker commit ${{ job.services.webserver.id }} ${{ env.IMAGE_TAG }} docker push ${{ env.IMAGE_TAG }} - - name: Push to tag latest - if: endsWith(inputs.webserver_image, ':8.3-base-ai') + - name: Push to tag latest (CMS only) + if: endsWith(inputs.webserver_image, ':8.3-base-ai') && inputs.image_tag_suffix == 'cms' + run: | + docker commit ${{ job.services.webserver.id }} drupalforge/drupalpod-ai-qa:latest + docker push drupalforge/drupalpod-ai-qa:latest + + - name: Push to tag core (Core only) + if: endsWith(inputs.webserver_image, ':8.3-base-ai') && inputs.image_tag_suffix == 'core' run: | - docker commit ${{ job.services.webserver.id }} drupalforge/drupalpod:latest - docker push drupalforge/drupalpod:latest + docker commit ${{ job.services.webserver.id }} drupalforge/drupalpod-ai-qa:core + docker push drupalforge/drupalpod-ai-qa:core diff --git a/.github/workflows/docker-publish-images.yml b/.github/workflows/docker-publish-images.yml index 4b1722f1..c6b4d61c 100644 --- a/.github/workflows/docker-publish-images.yml +++ b/.github/workflows/docker-publish-images.yml @@ -10,14 +10,34 @@ concurrency: permissions: actions: write jobs: - build-php-8_2: + build-php-8_2-cms: uses: ./.github/workflows/docker-publish-image.yml with: webserver_image: devpanel/php:8.2-base-ai - drupal_core_version: 10.5.4 + drupal_core_version: '1.x' + image_tag_suffix: cms secrets: inherit - build-php-8_3: + + build-php-8_2-core: + uses: ./.github/workflows/docker-publish-image.yml + with: + webserver_image: devpanel/php:8.2-base-ai + drupal_core_version: '11.x' + image_tag_suffix: core + secrets: inherit + + build-php-8_3-cms: + uses: ./.github/workflows/docker-publish-image.yml + with: + webserver_image: devpanel/php:8.3-base-ai + drupal_core_version: '1.x' + image_tag_suffix: cms + secrets: inherit + + build-php-8_3-core: uses: ./.github/workflows/docker-publish-image.yml with: webserver_image: devpanel/php:8.3-base-ai + drupal_core_version: '11.x' + image_tag_suffix: core secrets: inherit diff --git a/.gitignore b/.gitignore index 89cf65ea..713d379c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,24 @@ -# Drupal -/web/ -/vendor/ -composer.json -composer.lock -.editorconfig -.gitattributes +# Drupal installation (everything Drupal-related is in docroot/) +/docroot -# DrupalPod -.drupalpod_initiated -src/**/node_modules/ -src/**/package-lock.json -src/**/out/ -src/**/*.vsix -src/**/.vscode-test/ -drush/ -.ddev/config.gitpod.yaml +# Git submodules (dynamically generated - don't commit) +/.gitmodules +/repos + +# Dependencies managed by Composer (legacy - now in docroot/). +/recipes +/vendor + +# macOS +.DS_Store +.AppleDouble +.LSOverride +._* +.Spotlight-V100 +.Trashes + +# Windows +Thumbs.db +ehthumbs.db +Desktop.ini +$RECYCLE.BIN/ diff --git a/.gitmodules b/.gitmodules index 10908d5e..061d8206 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,3 +2,31 @@ path = repos/drupal url = https://git.drupalcode.org/project/drupal.git ignore = dirty +[submodule "repos/ai"] + path = repos/ai + url = https://git.drupalcode.org/project/ai.git + ignore = dirty +[submodule "repos/ai_provider_litellm"] + path = repos/ai_provider_litellm + url = https://git.drupalcode.org/project/ai_provider_litellm.git + ignore = dirty +[submodule "repos/ai_search"] + path = repos/ai_search + url = https://git.drupalcode.org/project/ai_search.git + ignore = dirty +[submodule "repos/ai_agents"] + path = repos/ai_agents + url = https://git.drupalcode.org/project/ai_agents.git + ignore = dirty +[submodule "docroot/repos/ai"] + path = docroot/repos/ai + url = https://git.drupalcode.org/project/ai.git +[submodule "docroot/repos/ai_provider_litellm"] + path = docroot/repos/ai_provider_litellm + url = https://git.drupalcode.org/project/ai_provider_litellm.git +[submodule "docroot/repos/ai_search"] + path = docroot/repos/ai_search + url = https://git.drupalcode.org/project/ai_search.git +[submodule "docroot/repos/ai_agents"] + path = docroot/repos/ai_agents + url = https://git.drupalcode.org/project/ai_agents.git diff --git a/.gitpod.yml b/.gitpod.yml deleted file mode 100644 index 6c358201..00000000 --- a/.gitpod.yml +++ /dev/null @@ -1,77 +0,0 @@ -image: drupalpod/drupalpod-gitpod-base:20250309 - -# DDEV and composer are running as part of the prebuild -# when starting a workspace all docker images are ready -tasks: - - init: | - .gitpod/utils/send-a-message-gcs.sh > /tmp/output1.txt - .gitpod/utils/ddev-in-gitpod-setup.sh - .gitpod/utils/set-base-environment.sh - command: | - # Temporary fix for wrong value of GITPOD_REPO_ROOT when opening a Gitpod snapshot - # Todo: remove this when this issue is resolved - https://github.com/gitpod-io/gitpod/issues/9804 - if [ "$GITPOD_REPO_ROOT" == '/workspace' ]; then - export GITPOD_REPO_ROOT="$THEIA_WORKSPACE_ROOT" - fi - .gitpod/utils/ddev-in-gitpod-setup.sh - .gitpod/utils/env-setup.sh - .gitpod/drupal/ssh/01-check-private-ssh.sh - .gitpod/drupal/drupalpod-setup/drupalpod-setup.sh - -# VScode xdebug extension -vscode: - extensions: - # PHP extensions. - - felixfbecker.php-debug - - wongjn.php-sniffer - - neilbrayfield.php-docblocker - - bmewburn.vscode-intelephense-client - - andrewdavidblum.drupal-smart-snippets - - # Twig extensions. - - mblode.twig-language-2 - - # Help extensions. - - drupal-mentoring.drupalpod-ext - - # Bash extensions. - - timonwong.shellcheck - - rogalmic.bash-debug - -ports: - # Used by JS projects - - port: 3000 - onOpen: ignore - # Used by DDEV - local db clients - - port: 3306 - name: Database - description: Access for local database clients - onOpen: ignore - # Used by JS projects - - port: 5000 - onOpen: ignore - # Used by MailHog - - port: 8027 - name: MailHog - description: MailHog - onOpen: ignore - # Used by phpMyAdmin - - port: 8036 - name: phpMyAdmin - description: phpMyAdmin - onOpen: ignore - # Direct-connect DDEV-webserver port that is the main port - - port: 8080 - name: website - description: website - onOpen: ignore - # Ignore host https port - - port: 8443 - name: (not used) - description: host https port - onOpen: ignore - # xdebug port - - port: 9003 - name: xdebug - description: xdebug - onOpen: ignore diff --git a/.gitpod/README.md b/.gitpod/README.md deleted file mode 100644 index 658c01bd..00000000 --- a/.gitpod/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# Setting up a new version for DrupalPod - -## Build custom Gitpod image - -1. Update `.gitpod/images/Dockerfile`: - - 1. Update `ddev` latest version. - 1. Update `minio` latest version. - 1. Update `gitui` latest version. - 1. Update `lazygit` latest version. - -1. Generate new custom docker image: - - 1. Run `docker login` to authenticate and push new images to docker hub. - 1. In `/.gitpod/images` run `./push.sh` command to build and push the new custom docker image. - 1. Confirm the process run without errors and that the new custom image gets uploaded to . - 1. Update `/.gitpod.yml` with the new image file. - -1. Push code, and re-open Gitpod workspace, to use latest custom docker image. - -1. Run manual prebuild (to load ddev's images) - -1. Confirm latest setup - 1. Open new workspace. - 1. Check if there are any updates (ie. DDEV's default settings files). - -1. Test various scenarios with DrupalPod browser extension - 1. Confirm core issues work as expected. - 1. Confirm contrib issue work as expected. - -1. Merge PR into `main` branch - -1. Confirm `main` branch work as expected 🎉 diff --git a/.gitpod/drupal/drupalpod-setup/cleanup.sh b/.gitpod/drupal/drupalpod-setup/cleanup.sh deleted file mode 100644 index c9546351..00000000 --- a/.gitpod/drupal/drupalpod-setup/cleanup.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Remove site that was installed before (for debugging) -rm -rf "${GITPOD_REPO_ROOT}"/web -rm -rf "${GITPOD_REPO_ROOT}"/vendor -rm -f "${GITPOD_REPO_ROOT}"/composer.json -rm -f "${GITPOD_REPO_ROOT}"/composer.lock diff --git a/.gitpod/drupal/drupalpod-setup/composer_setup.sh b/.gitpod/drupal/drupalpod-setup/composer_setup.sh deleted file mode 100644 index 4504c102..00000000 --- a/.gitpod/drupal/drupalpod-setup/composer_setup.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# For versions end with x - add `-dev` suffix (ie. 9.3.x-dev) -# For versions without x - add `~` prefix (ie. ~9.2.0) -d="$DP_CORE_VERSION" -case $d in -*.x) - install_version="$d"-dev - ;; -*) - install_version=~"$d" - ;; -esac - -# Create required composer.json and composer.lock files -cd "$GITPOD_REPO_ROOT" && time ddev . composer create -n --no-install drupal/recommended-project:"$install_version" temp-composer-files -cp "$GITPOD_REPO_ROOT"/temp-composer-files/* "$GITPOD_REPO_ROOT"/. -rm -rf "$GITPOD_REPO_ROOT"/temp-composer-files - -# Programmatically fix Composer 2.2 allow-plugins to avoid errors -ddev composer config --no-plugins allow-plugins.composer/installers true -ddev composer config --no-plugins allow-plugins.drupal/core-project-message true -ddev composer config --no-plugins allow-plugins.drupal/core-vendor-hardening true -ddev composer config --no-plugins allow-plugins.drupal/core-composer-scaffold true - -ddev composer config --no-plugins allow-plugins.dealerdirect/phpcodesniffer-composer-installer true -ddev composer config --no-plugins allow-plugins.phpstan/extension-installer true - -ddev composer config --no-plugins allow-plugins.mglaman/composer-drupal-lenient true - -ddev composer config --no-plugins allow-plugins.php-http/discovery true - -ddev composer config --no-plugins allow-plugins.tbachert/spi true - -# Add project source code as symlink (to repos/name_of_project) -# double quotes explained - https://stackoverflow.com/a/1250279/5754049 -if [ -n "$DP_PROJECT_NAME" ]; then - cd "${GITPOD_REPO_ROOT}" && - ddev composer config \ - repositories.core1 \ - '{"type": "path", "url": "repos/'"$DP_PROJECT_NAME"'", "options": {"symlink": true}}' - - cd "$GITPOD_REPO_ROOT" && - ddev composer config minimum-stability dev -fi diff --git a/.gitpod/drupal/drupalpod-setup/contrib_modules_setup.sh b/.gitpod/drupal/drupalpod-setup/contrib_modules_setup.sh deleted file mode 100644 index 6892b60a..00000000 --- a/.gitpod/drupal/drupalpod-setup/contrib_modules_setup.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Check if additional modules should be installed -export DEVEL_NAME="devel" -export DEVEL_PACKAGE="drupal/devel" - -export ADMIN_TOOLBAR_NAME="admin_toolbar_tools" -export ADMIN_TOOLBAR_PACKAGE="drupal/admin_toolbar" - -# TODO: once Drupalpod extension supports additional modules - remove these 2 lines -export DP_EXTRA_DEVEL=1 -export DP_EXTRA_ADMIN_TOOLBAR=1 - -# Adding support for composer-drupal-lenient - https://packagist.org/packages/mglaman/composer-drupal-lenient -if [[ "$DP_CORE_VERSION" =~ ^10(\..*)?$ ]]; then - if [ "$DP_PROJECT_TYPE" != "project_core" ]; then - export COMPOSER_DRUPAL_LENIENT=mglaman/composer-drupal-lenient - else - export COMPOSER_DRUPAL_LENIENT='' - fi -fi - -# Adding support for composer-drupal-lenient - https://packagist.org/packages/mglaman/composer-drupal-lenient -if [[ "$DP_CORE_VERSION" =~ ^11(\..*)?$ ]]; then - # admin_toolbar and devel are not compatible yet with Drupal 11 - export DP_EXTRA_ADMIN_TOOLBAR= - export DP_EXTRA_DEVEL= - if [ "$DP_PROJECT_TYPE" != "project_core" ]; then - export COMPOSER_DRUPAL_LENIENT=mglaman/composer-drupal-lenient - else - export COMPOSER_DRUPAL_LENIENT='' - fi -fi diff --git a/.gitpod/drupal/drupalpod-setup/ddev_setup.sh b/.gitpod/drupal/drupalpod-setup/ddev_setup.sh deleted file mode 100644 index 691a98c1..00000000 --- a/.gitpod/drupal/drupalpod-setup/ddev_setup.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# set PHP version, based on https://www.drupal.org/docs/getting-started/system-requirements/php-requirements#versions -major_version=$(echo "$DP_CORE_VERSION" | cut -d '.' -f 1) -minor_version=$(echo "$DP_CORE_VERSION" | cut -d '.' -f 2) - -# Before Drupal 10.2, we should use php 8.2, otherwise use php 8.3 -if (( major_version < 10 )) || { (( major_version == 10 )) && (( minor_version < 2 )); }; then - php_version="8.2" -else - php_version="8.3" -fi - -cat < "${GITPOD_REPO_ROOT}"/.ddev/config.gitpod.yaml -#ddev-gitpod-generated -php_version: "$php_version" -CONFIGEND - -time ddev start diff --git a/.gitpod/drupal/drupalpod-setup/drupal_setup_contrib.sh b/.gitpod/drupal/drupalpod-setup/drupal_setup_contrib.sh deleted file mode 100644 index 608e7d6d..00000000 --- a/.gitpod/drupal/drupalpod-setup/drupal_setup_contrib.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Drupal projects with no composer.json, bypass the symlink config, symlink has to be created manually. - -if [ "$DP_PROJECT_TYPE" == "project_module" ]; then - PROJECT_TYPE=modules -elif [ "$DP_PROJECT_TYPE" == "project_theme" ]; then - PROJECT_TYPE=themes -fi - -cat <"${GITPOD_REPO_ROOT}"/repos/add-project-as-symlink.sh -#!/usr/bin/env bash -# This file was dynamically generated by a script -echo "Replace project with a symlink" -rm -rf web/$PROJECT_TYPE/contrib/$DP_PROJECT_NAME -cd web/$PROJECT_TYPE/contrib && ln -s ../../../repos/$DP_PROJECT_NAME . -PROJECTASYMLINK - -chmod +x "${GITPOD_REPO_ROOT}"/repos/add-project-as-symlink.sh - -if [ -n "$COMPOSER_DRUPAL_LENIENT" ]; then - # Add composer_drupal_lenient for modules on Drupal 10 - cd "${GITPOD_REPO_ROOT}" && ddev composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/'"$DP_PROJECT_NAME"'"]' - cd "${GITPOD_REPO_ROOT}" && time ddev . composer require "$COMPOSER_DRUPAL_LENIENT" -fi -# Add the project to composer (it will get the version according to the branch under `/repo/name_of_project`) -cd "${GITPOD_REPO_ROOT}" && time ddev . composer require drupal/"$DP_PROJECT_NAME" --no-interaction --no-install diff --git a/.gitpod/drupal/drupalpod-setup/drupal_setup_core.sh b/.gitpod/drupal/drupalpod-setup/drupal_setup_core.sh deleted file mode 100644 index cea31188..00000000 --- a/.gitpod/drupal/drupalpod-setup/drupal_setup_core.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Add a special path when working on core contributions -# (Without it, /web/modules/contrib is not found by website) -cd "${GITPOD_REPO_ROOT}" && - ddev composer config \ - repositories.drupal-core2 \ - '{"type": "path", "url": "repos/drupal/core"}' - -cd "${GITPOD_REPO_ROOT}" && - ddev composer config \ - repositories.drupal-core3 \ - '{"type": "path", "url": "repos/drupal/composer/Metapackage/CoreRecommended"}' - -cd "${GITPOD_REPO_ROOT}" && - ddev composer config \ - repositories.drupal-core4 \ - '{"type": "path", "url": "repos/drupal/composer/Metapackage/DevDependencies"}' - -cd "${GITPOD_REPO_ROOT}" && - ddev composer config \ - repositories.drupal-core5 \ - '{"type": "path", "url": "repos/drupal/composer/Plugin/ProjectMessage"}' - -cd "${GITPOD_REPO_ROOT}" && - ddev composer config \ - repositories.drupal-core6 \ - '{"type": "path", "url": "repos/drupal/composer/Plugin/VendorHardening"}' - -# Removing the conflict part of composer -echo "$(cat composer.json | jq 'del(.conflict)' --indent 4)" >composer.json - -# If a core issue branch was chosen, we want the version of Drupal core that is in that issue branch -# This is very helpful for issues that started with previous Drupal core versions, and the issue version automatically got updated to latest current drupal version -if [ "$DP_PROJECT_TYPE" == "project_core" ] && [ -n "$DP_ISSUE_BRANCH" ]; then - time composer require drupal/core-recommended:* drupal/core-project-message:* drupal/core-composer-scaffold:* --no-update -fi - -# Only after composer update, /web/core get symlinked to /repos/drupal/core -# repos/drupal/core -> web/core -time composer update - -# vendor -> repos/drupal/vendor -if [ ! -L "$GITPOD_REPO_ROOT"/repos/drupal/vendor ]; then - cd "$GITPOD_REPO_ROOT"/repos/drupal && - ln -s ../../vendor . -fi - -# Create folders for running tests -mkdir -p "$GITPOD_REPO_ROOT"/web/sites/simpletest -mkdir -p "$GITPOD_REPO_ROOT"/web/sites/simpletest/browser_output - -# Symlink the simpletest folder into the Drupal core git repo. -# repos/drupal/sites/simpletest -> ../../../web/sites/simpletest -if [ ! -L "$GITPOD_REPO_ROOT"/repos/drupal/sites/simpletest ]; then - cd "$GITPOD_REPO_ROOT"/repos/drupal/sites && - ln -s ../../../web/sites/simpletest . -fi diff --git a/.gitpod/drupal/drupalpod-setup/drupalpod-setup.md b/.gitpod/drupal/drupalpod-setup/drupalpod-setup.md deleted file mode 100644 index f9047dd7..00000000 --- a/.gitpod/drupal/drupalpod-setup/drupalpod-setup.md +++ /dev/null @@ -1,18 +0,0 @@ -# File Structure - -The drupalpod-setup.sh script can be divided into several logical sections based on the tasks it performs. Here are some potential divisions: - -1. Environment Setup: This section sets up the environment variables and checks for certain conditions. It includes the loading of default environment variables, setting up the default setup during the prebuild process, and checking if additional modules should be installed. - -1. Composer Support: This section adds support for composer-drupal-lenient based on the Drupal core version. - -1. Drupal Setup: This section checks if the setup has already run once and if no special setup is set by the DrupalPod extension. If not, it performs a series of setup tasks, including adding git.drupal.org to known hosts, ignoring specific directories during Drupal core development, and getting the required repo ready. - -1. Drupal Installation: This section handles the installation of Drupal, including enabling extra modules, setting the default admin theme, enabling the requested module or theme, and updating the database if working on core. - -1. Post-Installation: This section takes a snapshot of the database, saves a file to mark the workspace as already initiated, and measures the script time. - -1. Sourced Files: - - `setup_env.sh`: Sets up default environment variables based on a `.env` file. - - `install_modules.sh`: Sets environment variables for installing additional modules in Drupal. - - `drupal_version_specifics.sh`: Adds support for the `composer-drupal-lenient` package based on the Drupal core version. diff --git a/.gitpod/drupal/drupalpod-setup/drupalpod-setup.sh b/.gitpod/drupal/drupalpod-setup/drupalpod-setup.sh deleted file mode 100755 index a9bc05d9..00000000 --- a/.gitpod/drupal/drupalpod-setup/drupalpod-setup.sh +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Initialize all variables with null if they do not exist -: "${DEBUG_SCRIPT:=}" -: "${GITPOD_HEADLESS:=}" -: "${DP_INSTALL_PROFILE:=}" -: "${DP_EXTRA_DEVEL:=}" -: "${DP_EXTRA_ADMIN_TOOLBAR:=}" -: "${DP_PROJECT_TYPE:=}" -: "${DEVEL_NAME:=}" -: "${DEVEL_PACKAGE:=}" -: "${ADMIN_TOOLBAR_NAME:=}" -: "${ADMIN_TOOLBAR_PACKAGE:=}" -: "${COMPOSER_DRUPAL_LENIENT:=}" -: "${DP_CORE_VERSION:=}" -: "${DP_ISSUE_BRANCH:=}" -: "${DP_ISSUE_FORK:=}" -: "${DP_MODULE_VERSION:=}" -: "${DP_PATCH_FILE:=}" - -# Assuming .sh files are in the same directory as this script -DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" - -if [ -n "$DEBUG_SCRIPT" ] || [ -n "$GITPOD_HEADLESS" ]; then - set -x -fi - -convert_version() { - local version=$1 - if [[ $version =~ "-" ]]; then - # Remove the part after the dash and replace the last numeric segment with 'x' - local base_version=${version%-*} - echo "${base_version%.*}.x" - else - echo "$version" - fi -} - -# Test cases -# echo $(convert_version "9.2.5-dev1") # Output: 9.2.x -# echo $(convert_version "9.2.5") # Output: 9.2.5 -# echo $(convert_version "10.1.0-beta1") # Output: 10.1.x -# echo $(convert_version "11.0-dev") # Output: 11.x - -# Skip setup if it already ran once and if no special setup is set by DrupalPod extension -if [ ! -f "${GITPOD_REPO_ROOT}"/.drupalpod_initiated ]; then - - # Set a default setup if project type wasn't specified - if [ -z "$DP_PROJECT_TYPE" ]; then - source "$DIR/fallback_setup.sh" - fi - - source "$DIR/git_setup.sh" - - # If this is an issue fork of Drupal core - set the drupal core version based on that issue fork - if [ "$DP_PROJECT_TYPE" == "project_core" ] && [ -n "$DP_ISSUE_FORK" ]; then - VERSION_FROM_GIT=$(grep 'const VERSION' "${GITPOD_REPO_ROOT}"/repos/drupal/core/lib/Drupal.php | awk -F "'" '{print $2}') - DP_CORE_VERSION=$(convert_version "$VERSION_FROM_GIT") - export DP_CORE_VERSION - fi - - source "$DIR/ddev_setup.sh" - - # Measure the time it takes to go through the script - script_start_time=$(date +%s) - - source "$DIR/contrib_modules_setup.sh" - source "$DIR/cleanup.sh" - source "$DIR/composer_setup.sh" - - if [ -n "$DP_PATCH_FILE" ]; then - echo Applying selected patch "$DP_PATCH_FILE" - cd "${WORK_DIR}" && curl "$DP_PATCH_FILE" | patch -p1 - fi - - # Prepare special setup to work with Drupal core - if [ "$DP_PROJECT_TYPE" == "project_core" ]; then - source "$DIR/drupal_setup_core.sh" - # Prepare special setup to work with Drupal contrib - elif [ -n "$DP_PROJECT_NAME" ]; then - source "$DIR/drupal_setup_contrib.sh" - fi - - time "${GITPOD_REPO_ROOT}"/.gitpod/drupal/install-essential-packages.sh - # Configure phpcs for drupal. - cd "$GITPOD_REPO_ROOT" && - vendor/bin/phpcs --config-set installed_paths vendor/drupal/coder/coder_sniffer - - # ddev config auto updates settings.php and generates settings.ddev.php - ddev config --auto - # New site install - time ddev drush si -y --account-pass=admin --site-name="DrupalPod" "$DP_INSTALL_PROFILE" - - # Install devel and admin_toolbar modules - if [ "$DP_EXTRA_DEVEL" != '1' ]; then - DEVEL_NAME= - fi - if [ "$DP_EXTRA_ADMIN_TOOLBAR" != '1' ]; then - ADMIN_TOOLBAR_NAME= - fi - - # Enable extra modules - cd "${GITPOD_REPO_ROOT}" && - ddev drush en -y \ - "$ADMIN_TOOLBAR_NAME" \ - "$DEVEL_NAME" - - # Enable the requested module - if [ "$DP_PROJECT_TYPE" == "project_module" ]; then - cd "${GITPOD_REPO_ROOT}" && ddev drush en -y "$DP_PROJECT_NAME" - fi - - # Enable the requested theme - if [ "$DP_PROJECT_TYPE" == "project_theme" ]; then - cd "${GITPOD_REPO_ROOT}" && ddev drush then -y "$DP_PROJECT_NAME" - cd "${GITPOD_REPO_ROOT}" && ddev drush config-set -y system.theme default "$DP_PROJECT_NAME" - fi - - # Take a snapshot - cd "${GITPOD_REPO_ROOT}" && ddev snapshot - echo "Your database state was locally saved, you can revert to it by typing:" - echo "ddev snapshot restore --latest" - - # Save a file to mark workspace already initiated - touch "${GITPOD_REPO_ROOT}"/.drupalpod_initiated - - # Finish measuring script time - script_end_time=$(date +%s) - runtime=$((script_end_time - script_start_time)) - echo "drupalpod-setup.sh script ran for" $runtime "seconds" -else - cd "${GITPOD_REPO_ROOT}" && ddev start -fi - -# Open internal preview browser with current website -preview diff --git a/.gitpod/drupal/drupalpod-setup/fallback_setup.sh b/.gitpod/drupal/drupalpod-setup/fallback_setup.sh deleted file mode 100644 index b444e567..00000000 --- a/.gitpod/drupal/drupalpod-setup/fallback_setup.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Set a default setup (when project type is not specified) -export DP_INSTALL_PROFILE='demo_umami' -export DP_PROJECT_TYPE='project_core' -export DP_PROJECT_NAME="drupal" -export DP_CORE_VERSION='11.2.5' -export DP_EXTRA_DEVEL=1 -export DP_EXTRA_ADMIN_TOOLBAR=1 diff --git a/.gitpod/drupal/drupalpod-setup/git_setup.sh b/.gitpod/drupal/drupalpod-setup/git_setup.sh deleted file mode 100644 index 3ec3947c..00000000 --- a/.gitpod/drupal/drupalpod-setup/git_setup.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Add git.drupal.org to known_hosts -if [ -z "$GITPOD_HEADLESS" ]; then - mkdir -p ~/.ssh - host=git.drupal.org - SSHKey=$(ssh-keyscan $host 2>/dev/null) - echo "$SSHKey" >>~/.ssh/known_hosts -fi - -# Ignore specific directories during Drupal core development -cp "${GITPOD_REPO_ROOT}"/.gitpod/drupal/templates/git-exclude.template "${GITPOD_REPO_ROOT}"/.git/info/exclude - -# Get the required repo ready -if [ "$DP_PROJECT_TYPE" == "project_core" ]; then - # Find if requested core version is dev or stable - d="$DP_CORE_VERSION" - case $d in - *.x) - # If dev - use git checkout origin/* - checkout_type=origin - ;; - *) - # stable - use git checkout tags/* - checkout_type=tags - ;; - esac - - # Use origin or tags in git checkout command - cd "${GITPOD_REPO_ROOT}"/repos/drupal && - git fetch origin && - git fetch --all --tags && - git checkout "$checkout_type"/"$DP_CORE_VERSION" - - # Ignore specific directories during Drupal core development - cp "${GITPOD_REPO_ROOT}"/.gitpod/drupal/templates/git-exclude.template "${GITPOD_REPO_ROOT}"/repos/drupal/.git/info/exclude -else - # If not core - clone selected project into /repos and remove drupal core - rm -rf "${GITPOD_REPO_ROOT}"/repos/drupal - if [ ! -d repos/"${DP_PROJECT_NAME}" ]; then - mkdir -p repos - cd "${GITPOD_REPO_ROOT}"/repos && time git clone https://git.drupalcode.org/project/"$DP_PROJECT_NAME".git - fi -fi - -# Set WORK_DIR -export WORK_DIR="${GITPOD_REPO_ROOT}"/repos/$DP_PROJECT_NAME - -# Dynamically generate .gitmodules file -cat <"${GITPOD_REPO_ROOT}"/.gitmodules -# This file was dynamically generated by a script -[submodule "$DP_PROJECT_NAME"] -path = repos/$DP_PROJECT_NAME -url = https://git.drupalcode.org/project/$DP_PROJECT_NAME.git -ignore = dirty -GITMODULESEND - -# Checkout specific branch only if there's issue_branch -if [ -n "$DP_ISSUE_BRANCH" ]; then - # If branch already exist only run checkout, - if cd "${WORK_DIR}" && git show-ref -q --heads "$DP_ISSUE_BRANCH"; then - cd "${WORK_DIR}" && git checkout "$DP_ISSUE_BRANCH" - else - cd "${WORK_DIR}" && git remote add "$DP_ISSUE_FORK" https://git.drupalcode.org/issue/"$DP_ISSUE_FORK".git - cd "${WORK_DIR}" && git fetch "$DP_ISSUE_FORK" - cd "${WORK_DIR}" && git checkout -b "$DP_ISSUE_BRANCH" --track "$DP_ISSUE_FORK"/"$DP_ISSUE_BRANCH" - fi -elif [ -n "$DP_MODULE_VERSION" ] && [ "$DP_PROJECT_TYPE" != "project_core" ]; then - cd "${WORK_DIR}" && git checkout "$DP_MODULE_VERSION" -fi diff --git a/.gitpod/drupal/install-essential-packages.sh b/.gitpod/drupal/install-essential-packages.sh deleted file mode 100755 index 590c551d..00000000 --- a/.gitpod/drupal/install-essential-packages.sh +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/env bash -set -eu -o pipefail - -# Initialize all variables with null if they do not exist -: "${DEBUG_SCRIPT:=}" -: "${GITPOD_HEADLESS:=}" -: "${DP_EXTRA_DEVEL:=}" -: "${DP_EXTRA_ADMIN_TOOLBAR:=}" -: "${DEVEL_PACKAGE:=}" -: "${ADMIN_TOOLBAR_PACKAGE:=}" - -if [ -n "$DEBUG_SCRIPT" ] || [ -n "$GITPOD_HEADLESS" ]; then - set -x -fi - -# Install devel and admin_toolbar modules -if [ "$DP_EXTRA_DEVEL" != '1' ]; then - DEVEL_PACKAGE= -fi -if [ "$DP_EXTRA_ADMIN_TOOLBAR" != '1' ]; then - ADMIN_TOOLBAR_PACKAGE= -fi - -cd "${GITPOD_REPO_ROOT}" && time ddev . composer require --dev "drupal/core-dev":* "phpspec/prophecy-phpunit":^2 -W --no-install -cd "${GITPOD_REPO_ROOT}" && time ddev . composer require "drush/drush" "drupal/coder" "$DEVEL_PACKAGE" "$ADMIN_TOOLBAR_PACKAGE" - -# Only for Drupal core - apply special patch -if [ "$DP_PROJECT_TYPE" == "project_core" ]; then - # Patch the scaffold index.php and update.php files - # See https://www.drupal.org/project/drupal/issues/3188703 - # See https://www.drupal.org/project/drupal/issues/1792310 - echo "$(cat composer.json | jq '.scripts."post-install-cmd" |= . + ["src/composer-drupal-core-setup/patch-core-index-and-update.sh"]')" >composer.json - echo "$(cat composer.json | jq '.scripts."post-update-cmd" |= . + ["src/composer-drupal-core-setup/patch-core-index-and-update.sh"]')" >composer.json - - # Run the patch once - time src/composer-drupal-core-setup/patch-core-index-and-update.sh - - # Get the major version of 'drush/drush' - drush_major_version=$(composer show drush/drush --no-ansi | awk '/versions/ {print $NF}' | cut -d '.' -f1) - - drush_command_dir="$GITPOD_REPO_ROOT/drush/Commands/core_development" - mkdir -p "$drush_command_dir" - - # Copy the correct version of DevelopmentProjectCommands.php file to the drush commands directory - cp "$GITPOD_REPO_ROOT/src/drush-commands-core-development/$drush_major_version/DevelopmentProjectCommands.php" "$drush_command_dir/." -else - # Only for contrib - add project as symlink - - echo "$(cat composer.json | jq '.scripts."post-install-cmd" |= . + ["repos/add-project-as-symlink.sh"]')" >composer.json - echo "$(cat composer.json | jq '.scripts."post-update-cmd" |= . + ["repos/add-project-as-symlink.sh"]')" >composer.json - time repos/add-project-as-symlink.sh -fi diff --git a/.gitpod/drupal/ssh/00-interactive-ssh-setup.sh b/.gitpod/drupal/ssh/00-interactive-ssh-setup.sh deleted file mode 100755 index 3b2df726..00000000 --- a/.gitpod/drupal/ssh/00-interactive-ssh-setup.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash - - -# Copy your public key -# php -S localhost:8000 id_rsa.backup & sleep 3 & gp preview $(gp url 8000) -# killall php - -# while-menu-dialog: a menu driven system information program - -DIALOG_CANCEL=1 -DIALOG_ESC=255 -HEIGHT=0 -WIDTH=0 - -display_result() { - dialog --title "$1" \ - --no-collapse \ - --msgbox "$result" 0 0 -} - -while true; do - exec 3>&1 - selection=$(dialog \ - --backtitle "Installer/Services menu" \ - --title "Menu" \ - --clear \ - --cancel-label "Exit" \ - --menu "Please select:" $HEIGHT $WIDTH 4 \ - "1" "Setup SSH" \ - 2>&1 1>&3) - exit_status=$? - exec 3>&- - case $exit_status in - $DIALOG_CANCEL) - clear - echo "Program terminated." - exit - ;; - $DIALOG_ESC) - clear - echo "Program aborted." >&2 - exit 1 - ;; - esac - case $selection in - 0 ) - clear - echo "Program terminated." - ;; - 1 ) - result=$(code .gitpod/drupal/ssh/instructions-template.md) - display_result "If you completed the instructions above - click OK" - .gitpod/drupal/ssh/04-confirm-ssh-setup.sh - # gp preview https://drupal.org/user --external - ;; - esac -done - diff --git a/.gitpod/drupal/ssh/01-check-private-ssh.sh b/.gitpod/drupal/ssh/01-check-private-ssh.sh deleted file mode 100755 index 0cd3c587..00000000 --- a/.gitpod/drupal/ssh/01-check-private-ssh.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -# Check if ~/.ssh/id_rsa already exist -if [ -f ~/.ssh/id_rsa ]; then - echo "No need to setup a key, SSH key already exists." -else - if [ -z "$DRUPAL_SSH_KEY" ]; then - # DRUPAL_SSH_KEY environment variable is not set, check if SSH ley was create during this session - if [ -f /workspace/id_rsa ] ; then - # Edge case where user already setup key in workspace that timed out. - echo "No need to setup a key, SSH key found." - mkdir -p ~/.ssh - cp /workspace/id_rsa ~/.ssh/. - fi - else - echo "Setting SSH key from environment variable" - mkdir -p ~/.ssh - printenv DRUPAL_SSH_KEY > ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - fi - # Ask for SSH keyphrase only once - if ssh-add -l > /dev/null ; then - eval "$(ssh-agent -s)" > /dev/null - ssh-add -q ~/.ssh/id_rsa - fi -fi diff --git a/.gitpod/drupal/ssh/02-setup-private-ssh.sh b/.gitpod/drupal/ssh/02-setup-private-ssh.sh deleted file mode 100755 index 48712708..00000000 --- a/.gitpod/drupal/ssh/02-setup-private-ssh.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -SSH_SETUP_REQUIRED=true - -# Add git.drupal.org to known_hosts -mkdir -p ~/.ssh -host=git.drupal.org -SSHKey=$(ssh-keyscan $host 2> /dev/null) -echo "$SSHKey" >> ~/.ssh/known_hosts - -# Validate private SSH key in Gitpod with public SSH key in drupal.org -if ssh -T git@git.drupal.org; then - read -r -p "SSH key was already confirmed with Drupal.org, are you sure you want to recreate SSH key? [y/N]" setup_ssh - if [ "$setup_ssh" != "y" ] && [ "$setup_ssh" != "Y" ]; then - SSH_SETUP_REQUIRED=false - fi -fi - -if $SSH_SETUP_REQUIRED; then - # Create ssh key pairing + instructions to paste public key in drupal.org - ssh-keygen -q -t rsa -b 4096 -f ~/.ssh/id_rsa - .gitpod/drupal/ssh/03-generate-drupal-ssh-instructions.sh - echo "Follow instructions for copying public key to Drupal" - echo "Test SSH by running .gitpod/drupal/ssh/04-confirm-ssh-setup.sh" -fi diff --git a/.gitpod/drupal/ssh/03-generate-drupal-ssh-instructions.sh b/.gitpod/drupal/ssh/03-generate-drupal-ssh-instructions.sh deleted file mode 100755 index 3c0b1044..00000000 --- a/.gitpod/drupal/ssh/03-generate-drupal-ssh-instructions.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -# create instructions file with user's public key -cat ~/.ssh/id_rsa.pub > /workspace/public_key.md -cat .gitpod/drupal/ssh/instructions-template.md > /workspace/drupal-public-key-setup.md -cat ~/.ssh/id_rsa.pub >> /workspace/drupal-public-key-setup.md - -gp open /workspace/drupal-public-key-setup.md diff --git a/.gitpod/drupal/ssh/04-confirm-ssh-setup.sh b/.gitpod/drupal/ssh/04-confirm-ssh-setup.sh deleted file mode 100755 index 7a903d02..00000000 --- a/.gitpod/drupal/ssh/04-confirm-ssh-setup.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -# Add git.drupal.org to known_hosts -mkdir -p ~/.ssh -host=git.drupal.org -SSHKey=$(ssh-keyscan $host 2> /dev/null) -echo "$SSHKey" >> ~/.ssh/known_hosts - -# Ask for SSH keyphrase only once -if ssh-add -l > /dev/null ; then - eval "$(ssh-agent -s)" > /dev/null - ssh-add -q ~/.ssh/id_rsa -fi - -# Validate private SSH key in Gitpod with public SSH key in drupal.org -if ssh -T git@git.drupal.org; then - echo "Setup was succesful, saving your private key in Gitpod" - # Set private SSH key as Gitpod variable anvironment - gp env "DRUPAL_SSH_KEY=$(cat ~/.ssh/id_rsa)" > /dev/null - # Copy key to /workspace in case this workspace times out - cp ~/.ssh/id_rsa /workspace/. - # Set repo remote branch to SSH (in case it was added as HTTPS) - .gitpod/drupal/ssh/05-set-repo-as-ssh.sh -else - if [ ! -f ~/.ssh/id_rsa.pub ] ; then - echo "Setup failed, create private key again" - rm -f /workspace/id_rsa - rm -rf ~/.ssh - .gitpod/drupal/ssh/02-setup-private-ssh.sh - else - echo "Setup failed, please confirm you copied public key to Drupal" - .gitpod/drupal/ssh/03-generate-drupal-ssh-instructions.sh - fi -fi diff --git a/.gitpod/drupal/ssh/05-set-repo-as-ssh.sh b/.gitpod/drupal/ssh/05-set-repo-as-ssh.sh deleted file mode 100755 index 7b57b25d..00000000 --- a/.gitpod/drupal/ssh/05-set-repo-as-ssh.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -# Set a specific branch if there's issue_fork -if [ -n "$DP_ISSUE_FORK" ]; then - cd "${GITPOD_REPO_ROOT}"/repos/"$DP_PROJECT_NAME" && git remote set-url "$DP_ISSUE_FORK" git@git.drupal.org:issue/"$DP_ISSUE_FORK".git -fi diff --git a/.gitpod/drupal/ssh/instructions-template.md b/.gitpod/drupal/ssh/instructions-template.md deleted file mode 100644 index e8434fde..00000000 --- a/.gitpod/drupal/ssh/instructions-template.md +++ /dev/null @@ -1,14 +0,0 @@ -# One-time public key setup -1. Press the OK button in the Terminal below and follow the instructions. -2. Open https://www.drupal.org/user -3. Click on "Git Access" -4. Create a Git username if you don't have one. -5. Click on "SSH keys", -6. Click on "Add a public key" -7. Copy the content below -8. Paste it inside "Key" field -9. Click on "Save" button. -10. Run in Gitpod terminal `.gitpod/drupal/ssh/04-confirm-ssh-setup.sh` - - - diff --git a/.gitpod/drupal/templates/git-exclude.template b/.gitpod/drupal/templates/git-exclude.template deleted file mode 100644 index 1c6ce4a2..00000000 --- a/.gitpod/drupal/templates/git-exclude.template +++ /dev/null @@ -1,10 +0,0 @@ -# git ls-files --others --exclude-from=.git/info/exclude -# Lines that start with '#' are comments. -# For a project mostly in C, the following would be a good set of -# exclude patterns (uncomment them if you want to use them): -# *.[oa] -# *~ -# Exclude the following directories for Drupal core development -vendor -sites/simpletest -sites/default/settings.php diff --git a/.gitpod/images/Dockerfile b/.gitpod/images/Dockerfile deleted file mode 100644 index 298779ba..00000000 --- a/.gitpod/images/Dockerfile +++ /dev/null @@ -1,40 +0,0 @@ -FROM gitpod/workspace-base as workspace-base -SHELL ["/bin/bash", "-c"] - -RUN sudo apt-get -qq update - -# Install dialog (interactive script) -RUN sudo apt-get -qq install -y dialog - -# Install DDEV -USER gitpod -# Add DDEV’s GPG key to your keyring -RUN curl -fsSL https://pkg.ddev.com/apt/gpg.key | gpg --dearmor | sudo tee /etc/apt/keyrings/ddev.gpg > /dev/null -# Add DDEV releases to your package repository -RUN echo "deb [signed-by=/etc/apt/keyrings/ddev.gpg] https://pkg.ddev.com/apt/ * *" | sudo tee /etc/apt/sources.list.d/ddev.list >/dev/null -# Update package information and install DDEV -RUN sudo apt-get update && sudo apt-get install -y ddev - -# Install GitUI (terminal-ui for git) -ARG GITUI_VERSION=v0.27.0 -RUN wget https://github.com/extrawurst/gitui/releases/download/${GITUI_VERSION}/gitui-linux-x86_64.tar.gz -P /tmp -RUN sudo tar xzf /tmp/gitui-linux-x86_64.tar.gz -C /usr/bin - -# Install LazyGit (terminal-ui for git) -ARG LAZYGIT_VERSION=0.48.0 -RUN wget https://github.com/jesseduffield/lazygit/releases/download/v${LAZYGIT_VERSION}/lazygit_${LAZYGIT_VERSION}_Linux_x86_64.tar.gz -P /tmp -RUN tar -C /tmp -xf /tmp/lazygit_${LAZYGIT_VERSION}_Linux_x86_64.tar.gz -RUN sudo install /tmp/lazygit /usr/local/bin - -# (get latest Minio version from https://dl.min.io/client/mc/release/linux-amd64/) -# Install Minio client -ARG MINIO_CLIENT_VERSION=mcli_20250221160046.0.0_amd64.deb -RUN wget https://dl.min.io/client/mc/release/linux-amd64/${MINIO_CLIENT_VERSION} -RUN sudo dpkg -i ${MINIO_CLIENT_VERSION} -RUN sudo mv /usr/local/bin/mcli /usr/local/bin/mc - -# End workspace-base - -FROM scratch as drupalpod-gitpod-base -SHELL ["/bin/bash", "-c"] -COPY --from=workspace-base / / diff --git a/.gitpod/images/push.sh b/.gitpod/images/push.sh deleted file mode 100755 index 8a146fb2..00000000 --- a/.gitpod/images/push.sh +++ /dev/null @@ -1,25 +0,0 @@ -#!/bin/bash -set -eu -o pipefail -# if [ -n "$DEBUG_SCRIPT" ]; then -# set -x -# fi - -# Run `docker login` to authenticate and push new images to docker hub -# Update /.gitpod.yml with the new image file - -# "%Y-%m-%d" -TODAY=$(date +"%Y%m%d") -REPO_NAME=drupalpod/drupalpod-gitpod-base -DOCKER_REPO="$REPO_NAME":"$TODAY" -DOCKER_REPO_LATEST="$REPO_NAME":latest - -echo "Pushing ${DOCKER_REPO}" -set -x -# Build only current architecture and load into docker -# docker buildx build -t "${DOCKER_REPO}" --push --target=drupalpod-gitpod-base --platform=linux/amd64 . -docker build --target drupalpod-gitpod-base -t "${DOCKER_REPO}" -t "${DOCKER_REPO_LATEST}" . -docker image push "${DOCKER_REPO}" -docker image push "${DOCKER_REPO_LATEST}" - -# docker run -it --rm bash -# docker image inspect diff --git a/.gitpod/utils/ddev-in-gitpod-setup.sh b/.gitpod/utils/ddev-in-gitpod-setup.sh deleted file mode 100755 index c03dfe6f..00000000 --- a/.gitpod/utils/ddev-in-gitpod-setup.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ] || [ -n "$GITPOD_HEADLESS" ]; then - set -x -fi - -# Misc housekeeping before start -ddev config global --instrumentation-opt-in=true diff --git a/.gitpod/utils/env-setup.sh b/.gitpod/utils/env-setup.sh deleted file mode 100755 index 84361ac7..00000000 --- a/.gitpod/utils/env-setup.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -cd "${GITPOD_REPO_ROOT}"/.gitpod/utils/script-templates || exit - -# Create a phpstorm command -sudo cp phpstorm.template.sh /usr/local/bin/phpstorm - -# Create a preview command -sudo cp preview.template.sh /usr/local/bin/preview - -# Create a protect-my-git command -sudo cp protect-my-git.template.sh /usr/local/bin/protect-my-git - -# Create php command (run php inside ddev container) -sudo cp ddev-php.template.sh /usr/local/bin/php - -# Create drush command (run drush inside ddev container) -sudo cp ddev-drush.template.sh /usr/local/bin/drush - -# Create yarn command (run yarn inside ddev container) -sudo cp ddev-yarn.template.sh /usr/local/bin/yarn - -# Create composer command (run composer inside ddev container) -sudo cp ddev-composer.template.sh /usr/local/bin/composer - -# Create node command (run composer inside ddev container) -sudo cp ddev-node.template.sh /usr/local/bin/node - -# Create nvm command (run composer inside ddev container) -sudo cp ddev-nvm.template.sh /usr/local/bin/nvm - -# Create npx command (run composer inside ddev container) -sudo cp ddev-npx.template.sh /usr/local/bin/npx diff --git a/.gitpod/utils/script-templates/ddev-composer.template.sh b/.gitpod/utils/script-templates/ddev-composer.template.sh deleted file mode 100755 index 138f48b2..00000000 --- a/.gitpod/utils/script-templates/ddev-composer.template.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -echo "Notice: running 'composer $*' in ddev" -ddev exec_d composer "$@" diff --git a/.gitpod/utils/script-templates/ddev-drush.template.sh b/.gitpod/utils/script-templates/ddev-drush.template.sh deleted file mode 100755 index 7ac0f909..00000000 --- a/.gitpod/utils/script-templates/ddev-drush.template.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -echo "Notice: running 'drush $*' in ddev" -ddev exec_d drush "$@" diff --git a/.gitpod/utils/script-templates/ddev-node.template.sh b/.gitpod/utils/script-templates/ddev-node.template.sh deleted file mode 100755 index 5a309527..00000000 --- a/.gitpod/utils/script-templates/ddev-node.template.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -echo "Notice: running 'node $*' in ddev" -ddev exec_d node "$@" diff --git a/.gitpod/utils/script-templates/ddev-npx.template.sh b/.gitpod/utils/script-templates/ddev-npx.template.sh deleted file mode 100755 index 73eeeee0..00000000 --- a/.gitpod/utils/script-templates/ddev-npx.template.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -echo "Notice: running 'npx $*' in ddev" -ddev exec_d npx "$@" diff --git a/.gitpod/utils/script-templates/ddev-nvm.template.sh b/.gitpod/utils/script-templates/ddev-nvm.template.sh deleted file mode 100755 index acd5c457..00000000 --- a/.gitpod/utils/script-templates/ddev-nvm.template.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -echo "Notice: running 'nvm $*' in ddev" -ddev exec_d nvm "$@" diff --git a/.gitpod/utils/script-templates/ddev-php.template.sh b/.gitpod/utils/script-templates/ddev-php.template.sh deleted file mode 100755 index 610926b9..00000000 --- a/.gitpod/utils/script-templates/ddev-php.template.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -echo "Notice: running 'php $*' in ddev" -ddev exec_d php "$@" diff --git a/.gitpod/utils/script-templates/ddev-yarn.template.sh b/.gitpod/utils/script-templates/ddev-yarn.template.sh deleted file mode 100755 index d3ffa815..00000000 --- a/.gitpod/utils/script-templates/ddev-yarn.template.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash -echo "Notice: running 'yarn $*' in ddev" -ddev exec_d yarn "$@" diff --git a/.gitpod/utils/script-templates/phpstorm.template.sh b/.gitpod/utils/script-templates/phpstorm.template.sh deleted file mode 100755 index 48c04d26..00000000 --- a/.gitpod/utils/script-templates/phpstorm.template.sh +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -if [ ! -x ~/.projector/configs/PhpStorm/run.sh ]; then - echo "PhpStorm runner not found" && exit 1 -fi - -# When port 9999 is ready - open that port in a new browser tab -gp await-port 9999 && gp preview "$(gp url 9999)" --external & - -# Run PHPStorm -~/.projector/configs/PhpStorm/run.sh "$GITPOD_REPO_ROOT" diff --git a/.gitpod/utils/script-templates/preview.template.sh b/.gitpod/utils/script-templates/preview.template.sh deleted file mode 100755 index 916a7fc7..00000000 --- a/.gitpod/utils/script-templates/preview.template.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -# Preview port 8080 -gp preview "$(gp url 8080)" diff --git a/.gitpod/utils/script-templates/protect-my-git.template.sh b/.gitpod/utils/script-templates/protect-my-git.template.sh deleted file mode 100755 index d9e7f828..00000000 --- a/.gitpod/utils/script-templates/protect-my-git.template.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -# Remove access to user's Git credentials (restore by restart workspace) -git config --global --unset credential.helper diff --git a/.gitpod/utils/send-a-message-gcs.sh b/.gitpod/utils/send-a-message-gcs.sh deleted file mode 100755 index 5c53aba5..00000000 --- a/.gitpod/utils/send-a-message-gcs.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bash - -# Get current user and current branch -branch_user="$GITPOD_GIT_USER_NAME, $(git branch --show-current)" - -# Load env vars during prebuild using `gp env` command -if [ -z "$IFTTT_TOKEN" ]; then - eval "$(gp env -e | grep IFTTT_TOKEN)" -fi - -# Load env vars during prebuild using `gp env` command -if [ -z "$DP_GOOGLE_ACCESS_KEY" ]; then - eval "$(gp env -e | grep DP_GOOGLE_ACCESS_KEY)" -fi - -# Load env vars during prebuild using `gp env` command -if [ -z "$DP_GOOGLE_SECRET" ]; then - eval "$(gp env -e | grep DP_GOOGLE_SECRET)" -fi - -# Establish connection with Google Cloud through Minio client -mc config host add gcs https://storage.googleapis.com "$DP_GOOGLE_ACCESS_KEY" "$DP_GOOGLE_SECRET" - -# If there's a problem send the error code -if mc find gcs/drupalpod/ready-made-envs.tar.gz; then - message="Success: Google Cloud is ready" -else - message="Error: Envs file wasn't found, it will be recreated" -fi - -# Send a message through IFTTT -curl -X POST -H "Content-Type: application/json" -d "{\"value1\":\"$branch_user\",\"value2\":\"$message\"}" https://maker.ifttt.com/trigger/drupalpod_prebuild_initiated/with/key/"$IFTTT_TOKEN" diff --git a/.gitpod/utils/send-a-message-gitpod.sh b/.gitpod/utils/send-a-message-gitpod.sh deleted file mode 100755 index b0de49c8..00000000 --- a/.gitpod/utils/send-a-message-gitpod.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env bash - -# Get current user and current branch -branch_user="$GITPOD_GIT_USER_NAME, $(git branch --show-current)" - -# Load env vars during prebuild using `gp env` command -if [ -z "$DP_READY_MADE_ENVS_URL" ]; then - eval "$(gp env -e | grep DP_READY_MADE_ENVS_URL)" -fi - -# Load env vars during prebuild using `gp env` command -if [ -z "$IFTTT_TOKEN" ]; then - eval "$(gp env -e | grep IFTTT_TOKEN)" -fi - -# Check the status of ready-made envs file -# https://stackoverflow.com/a/53358157/5754049 -url_status=$(wget --server-response --spider --quiet "${DP_READY_MADE_ENVS_URL}" 2>&1 | awk 'NR==1{print $2}') - -# If there's a problem send the error code -if [ "$url_status" = '200' ]; then - message="100%" -else - message="Error: $url_status - $DP_READY_MADE_ENVS_URL" -fi - -# Send a message through IFTTT -curl -X POST -H "Content-Type: application/json" -d "{\"value1\":\"$branch_user\",\"value2\":\"$message\"}" https://maker.ifttt.com/trigger/drupalpod_prebuild_initiated/with/key/"$IFTTT_TOKEN" diff --git a/.gitpod/utils/set-base-environment.sh b/.gitpod/utils/set-base-environment.sh deleted file mode 100755 index 6305af81..00000000 --- a/.gitpod/utils/set-base-environment.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -if [ -n "$DEBUG_SCRIPT" ]; then - set -x -fi - -# Load default envs -# export "$(grep -v '^#' "$GITPOD_REPO_ROOT"/.env | xargs -d '\n')" - -# Clone Drupal core repo -mkdir -p "${GITPOD_REPO_ROOT}"/repos -cd "${GITPOD_REPO_ROOT}"/repos && time git clone https://git.drupalcode.org/project/drupal.git diff --git a/.vscode/launch.json b/.vscode/launch.json index 685be73f..923c99bf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,21 +4,49 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { - "name": "Listen for XDebug", + "name": "Listen for Xdebug", "type": "php", "request": "launch", - "hostname": "0.0.0.0", "port": 9003, "pathMappings": { - "/var/www/html": "${workspaceRoot}" + "/var/www/html": "${workspaceFolder}" } }, { - "type": "bashdb", + "name": "Launch currently open script", + "type": "php", + "request": "launch", + "program": "${file}", + "cwd": "${fileDirname}", + "port": 0, + "runtimeArgs": [ + "-dxdebug.start_with_request=yes" + ], + "env": { + "XDEBUG_MODE": "debug,develop", + "XDEBUG_CONFIG": "client_port=${port}" + } + }, + { + "name": "Launch Built-in web server", + "type": "php", "request": "launch", - "name": "Bash-Debug (simplest configuration)", - "program": "${file}" + "runtimeArgs": [ + "-dxdebug.mode=debug", + "-dxdebug.start_with_request=yes", + "-S", + "localhost:0" + ], + "program": "", + "cwd": "${workspaceRoot}", + "port": 9003, + "serverReadyAction": { + "pattern": "Development Server \\(http://localhost:([0-9]+)\\) started", + "uriFormat": "http://localhost:%s", + "action": "openExternally" + } } ] } diff --git a/Drupal Core Development Composer Project.md b/Drupal Core Development Composer Project.md deleted file mode 100644 index 608ae7de..00000000 --- a/Drupal Core Development Composer Project.md +++ /dev/null @@ -1,211 +0,0 @@ -# Drupal Core Development Composer Project - -This is a Composer project template for developing Drupal core. - -It allows: - -- a clean git clone of Drupal core. -- Composer dependencies of Drupal core are installed, so Drupal can be installed - and run as normal. -- other Composer packages you might want, such as Drush, Devel module, and Admin - Toolbar module, can be installed too, but don't affect the composer files - that are part of Drupal core. - -## Installation - -To install a Drupal project for working on Drupal core: - -``` -$ composer create-project joachim-n/drupal-core-development-project -``` - -Composer will clone Drupal core into a 'repos/drupal' directory within the -project, and then symlink that into the project when it installs Drupal core. - -Once the installation is complete, you can install Drupal as normal, either with -`drush si` or with the web UI. - -## Limitations - -During some Composer commands you may see multiple copies of this error message: - -> Could not scan for classes inside [Drupal class filename]. - -These are harmless and can be ignored. - -## Developing Drupal core - -You can use the Drupal core git clone at 'repos/drupal/' in any way you like: -create feature branches, clone from drupal.org issue forks, and so on. Changes -you make to files in the git clone affect the project, since the git clone is -symlinked into it. - -### Managing the Composer project - -You can install any Composer packages you like, including Drupal contrib -modules, without affecting the git clone of Drupal core. To work with Composer, -you need to be in the root directory of the project. - -Changes to the git clone's composer.json will be taken into account by Composer. -So for example, if pulling from the main branch of Drupal core changes Composer -dependencies, and in particular if you change to a different core major or minor -branch, you should run `composer update` on the project to install these. - -### Running tests - -The following are required to run tests. - -#### PHPUnit configuration - -The simplest way to run tests with this setup is to put the phpunit.xml file in -the project root and then run tests from there: - -$ vendor/bin/phpunit web/core/PATH-TO-TEST-FILE/TestFile.php - -To set this up, copy Drupal core's sample phpunit.xml file to the project root: - -$ cp web/core/phpunit.xml.dist phpunit.xml - -Then change the `bootstrap` attribute so the path is correct: - -``` - repos/ai/composer.json <<'EOF' +{ + "name": "drupal/ai", + "require": { + "php": ">=8.1" + } +} +EOF + + cat > repos/ai_search/composer.json <<'EOF' +{ + "name": "drupal/ai_search", + "require": { + "drupal/ai": "^2.0" + } +} +EOF + + cat > repos/ai_provider_litellm/composer.json <<'EOF' +{ + "name": "drupal/ai_provider_litellm", + "require": { + "drupal/ai": "^1.2" + } +} +EOF + + cat > repos/ai_agents/composer.json <<'EOF' +{ + "name": "drupal/ai_agents", + "require": { + "drupal/ai": "^1.2" + } +} +EOF + + # Source ONLY helper functions (not main execution logic) + # Extract function definitions without running the script + eval "$(sed -n '/^get_ai_dependencies()/,/^}/p' .devpanel/clone_ai_modules.sh)" + eval "$(sed -n '/^get_compatible_version()/,/^}/p' .devpanel/clone_ai_modules.sh)" + eval "$(sed -n '/^is_compatible_with_ai()/,/^}/p' .devpanel/clone_ai_modules.sh)" +} + +teardown() { + # Clean up test directories + rm -rf repos/ai repos/ai_search repos/ai_provider_litellm repos/ai_agents +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Helper Function Tests +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "get_compatible_version extracts AI requirement from ai_search" { + result=$(get_compatible_version "repos/ai_search" "ai") + [ "$result" = "2.0.x" ] +} + +@test "get_compatible_version extracts AI requirement from ai_provider_litellm" { + result=$(get_compatible_version "repos/ai_provider_litellm" "ai") + [ "$result" = "1.2.x" ] +} + +@test "get_compatible_version returns empty for missing dependency" { + result=$(get_compatible_version "repos/ai_search" "nonexistent") + [ -z "$result" ] +} + +@test "is_compatible_with_ai: ai_provider_litellm compatible with AI 1.2.x" { + run is_compatible_with_ai "repos/ai_provider_litellm" "1.2.x" + [ "$status" -eq 0 ] +} + +@test "is_compatible_with_ai: ai_search incompatible with AI 1.2.x" { + run is_compatible_with_ai "repos/ai_search" "1.2.x" + [ "$status" -eq 1 ] +} + +@test "is_compatible_with_ai: ai_search compatible with AI 2.0.x" { + run is_compatible_with_ai "repos/ai_search" "2.0.x" + [ "$status" -eq 0 ] +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Version Constraint Parsing Tests +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "parse version constraint: ^2.0 → 2.0.x" { + # Create directory first, then composer.json + mkdir -p repos/test + cat > repos/test/composer.json <<'EOF' +{ + "require": { + "drupal/ai": "^2.0" + } +} +EOF + result=$(get_compatible_version "repos/test" "ai") + [ "$result" = "2.0.x" ] + rm -rf repos/test +} + +@test "parse version constraint: ^1.2.0 → 1.2.x" { + mkdir -p repos/test + cat > repos/test/composer.json <<'EOF' +{ + "require": { + "drupal/ai": "^1.2.0" + } +} +EOF + result=$(get_compatible_version "repos/test" "ai") + [ "$result" = "1.2.x" ] + rm -rf repos/test +} + +@test "parse version constraint: ~1.2.0 → 1.2.x" { + mkdir -p repos/test + cat > repos/test/composer.json <<'EOF' +{ + "require": { + "drupal/ai": "~1.2.0" + } +} +EOF + result=$(get_compatible_version "repos/test" "ai") + [ "$result" = "1.2.x" ] + rm -rf repos/test +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Edge Cases +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "is_compatible_with_ai: module without composer.json is compatible" { + mkdir -p repos/test_no_composer + run is_compatible_with_ai "repos/test_no_composer" "1.2.x" + [ "$status" -eq 0 ] + rm -rf repos/test_no_composer +} + +@test "is_compatible_with_ai: module without AI dependency is compatible" { + mkdir -p repos/test_no_ai_dep + cat > repos/test_no_ai_dep/composer.json <<'EOF' +{ + "require": { + "php": ">=8.1" + } +} +EOF + run is_compatible_with_ai "repos/test_no_ai_dep" "1.2.x" + [ "$status" -eq 0 ] + rm -rf repos/test_no_ai_dep +} + +@test "get_compatible_version: handles missing composer.json gracefully" { + mkdir -p repos/test_missing + result=$(get_compatible_version "repos/test_missing" "ai") + [ -z "$result" ] + rm -rf repos/test_missing +} diff --git a/tests/fallback_setup.bats b/tests/fallback_setup.bats new file mode 100644 index 00000000..16587390 --- /dev/null +++ b/tests/fallback_setup.bats @@ -0,0 +1,143 @@ +#!/usr/bin/env bats + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Test Suite for fallback_setup.sh +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +setup() { + export APP_ROOT="$(pwd)" + + # Unset all variables to test defaults + unset DP_STARTER_TEMPLATE + unset DP_VERSION + unset DP_AI_MODULE + unset DP_AI_MODULE_VERSION + unset DP_AI_MODULE_VERSION_EXPLICIT + unset DP_TEST_MODULE +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Default Value Tests +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "DP_STARTER_TEMPLATE defaults to 'cms'" { + source .devpanel/fallback_setup.sh + [ "$DP_STARTER_TEMPLATE" = "cms" ] +} + +@test "DP_VERSION defaults to '1.x' for CMS" { + export DP_STARTER_TEMPLATE="cms" + source .devpanel/fallback_setup.sh + [ "$DP_VERSION" = "1.x" ] +} + +@test "DP_VERSION defaults to '11.2.8' for core" { + export DP_STARTER_TEMPLATE="core" + source .devpanel/fallback_setup.sh + [ "$DP_VERSION" = "11.2.8" ] +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# AI Version Behavior Tests (Dependency-Driven, No Hardcoded Defaults) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "AI version defaults to empty (auto-detect from test module)" { + export DP_STARTER_TEMPLATE="cms" + export DP_VERSION="2.0.x" + source .devpanel/fallback_setup.sh + [ -z "$DP_AI_MODULE_VERSION" ] +} + +@test "AI version stays empty for CMS 1.x (no hardcoded defaults)" { + export DP_STARTER_TEMPLATE="cms" + export DP_VERSION="1.x" + source .devpanel/fallback_setup.sh + [ -z "$DP_AI_MODULE_VERSION" ] +} + +@test "AI version stays empty for Core 11.x (dependency-driven)" { + export DP_STARTER_TEMPLATE="core" + export DP_VERSION="11.x" + source .devpanel/fallback_setup.sh + [ -z "$DP_AI_MODULE_VERSION" ] +} + +@test "AI version stays empty for Core 10.x (dependency-driven)" { + export DP_STARTER_TEMPLATE="core" + export DP_VERSION="10.x" + source .devpanel/fallback_setup.sh + [ -z "$DP_AI_MODULE_VERSION" ] +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Explicit Version Tracking Tests +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "DP_AI_MODULE_VERSION_EXPLICIT=no when version is auto-detected" { + export DP_STARTER_TEMPLATE="core" + export DP_VERSION="11.x" + # Don't set DP_AI_MODULE_VERSION - let it auto-detect + source .devpanel/fallback_setup.sh + [ "$DP_AI_MODULE_VERSION_EXPLICIT" = "no" ] +} + +@test "DP_AI_MODULE_VERSION_EXPLICIT=yes when version is explicitly set" { + export DP_STARTER_TEMPLATE="core" + export DP_VERSION="11.x" + export DP_AI_MODULE_VERSION="2.0.x" # Explicitly set + source .devpanel/fallback_setup.sh + [ "$DP_AI_MODULE_VERSION_EXPLICIT" = "yes" ] + [ "$DP_AI_MODULE_VERSION" = "2.0.x" ] +} + +@test "Empty DP_AI_MODULE_VERSION stays empty (dependency-driven)" { + export DP_STARTER_TEMPLATE="core" + export DP_VERSION="11.x" + export DP_AI_MODULE_VERSION="" # Empty string + source .devpanel/fallback_setup.sh + [ "$DP_AI_MODULE_VERSION_EXPLICIT" = "no" ] + [ -z "$DP_AI_MODULE_VERSION" ] # Stays empty +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Version Validation Tests +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "Error: CMS template with core version (9.x)" { + export DP_STARTER_TEMPLATE="cms" + export DP_VERSION="9.x" + run source .devpanel/fallback_setup.sh + [ "$status" -eq 1 ] + [[ "$output" =~ "looks like a Drupal core version" ]] +} + +@test "Error: Core template with CMS version (1.x)" { + export DP_STARTER_TEMPLATE="core" + export DP_VERSION="1.x" + run source .devpanel/fallback_setup.sh + [ "$status" -eq 1 ] + [[ "$output" =~ "looks like a CMS version" ]] +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Install Profile Tests +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "CMS uses empty install profile (auto-detect drupal_cms_installer)" { + export DP_STARTER_TEMPLATE="cms" + source .devpanel/fallback_setup.sh + [ -z "$DP_INSTALL_PROFILE" ] +} + +@test "Core uses 'standard' install profile" { + export DP_STARTER_TEMPLATE="core" + source .devpanel/fallback_setup.sh + [ "$DP_INSTALL_PROFILE" = "standard" ] +} + +@test "Custom install profile can be overridden" { + export DP_STARTER_TEMPLATE="cms" + export DP_INSTALL_PROFILE="minimal" + source .devpanel/fallback_setup.sh + [ "$DP_INSTALL_PROFILE" = "minimal" ] +} diff --git a/tests/integration.bats b/tests/integration.bats new file mode 100644 index 00000000..7043f937 --- /dev/null +++ b/tests/integration.bats @@ -0,0 +1,215 @@ +#!/usr/bin/env bats + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Integration Tests - Verify Script Decisions (Not Actual Execution) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Tests verify the script makes correct decisions about: +# - Which modules to clone +# - What versions to use +# - When to fail vs continue +# - Compatibility filtering +# +# We DON'T actually clone repos or run composer - just verify logic! +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +setup() { + export APP_ROOT="$(pwd)" + + # Create mock repo structure with composer.json files + mkdir -p repos/{ai,ai_search,ai_provider_litellm,ai_agents} + + # ai_search requires AI ^2.0 + cat > repos/ai_search/composer.json <<'EOF' +{"require": {"drupal/ai": "^2.0"}} +EOF + + # ai_provider_litellm requires AI ^1.2 + cat > repos/ai_provider_litellm/composer.json <<'EOF' +{"require": {"drupal/ai": "^1.2"}} +EOF + + # ai_agents requires AI ^1.2 + cat > repos/ai_agents/composer.json <<'EOF' +{"require": {"drupal/ai": "^1.2"}} +EOF + + # Stub the clone_module function to just track what would be cloned + clone_module() { + local module_name=$1 + echo "WOULD_CLONE: $module_name" >&2 + + # Track in CLONED_AI_MODULES + if [ -z "$CLONED_AI_MODULES" ]; then + export CLONED_AI_MODULES="$module_name" + else + export CLONED_AI_MODULES="$CLONED_AI_MODULES,$module_name" + fi + } + export -f clone_module +} + +teardown() { + rm -rf repos +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Use Case: Test Module Specified (Auto-Detect AI Version) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "Test module ai_search → auto-detect AI 2.0.x" { + export DP_AI_MODULE="ai" + export DP_AI_MODULE_VERSION="" # Empty = auto-detect + export DP_AI_MODULE_VERSION_EXPLICIT="no" + export DP_TEST_MODULE="ai_search" + export DP_AI_MODULES="" + + # Load helper functions without executing main script + eval "$(sed -n '/^get_compatible_version()/,/^}/p' .devpanel/clone_ai_modules.sh)" + + # Get AI requirement from test module + ai_requirement=$(get_compatible_version "repos/ai_search" "ai") + + # Verify decision + [ "$ai_requirement" = "2.0.x" ] +} + +@test "Test module set + explicit AI version mismatch → should fail" { + export DP_AI_MODULE="ai" + export DP_AI_MODULE_VERSION="1.2.x" # Explicitly set + export DP_AI_MODULE_VERSION_EXPLICIT="yes" + export DP_TEST_MODULE="ai_search" # Needs 2.0.x + + eval "$(sed -n '/^get_compatible_version()/,/^}/p' .devpanel/clone_ai_modules.sh)" + + test_module_ai_requirement=$(get_compatible_version "repos/ai_search" "ai") + + # Verify we detected the conflict + [ "$test_module_ai_requirement" = "2.0.x" ] + [ "$DP_AI_MODULE_VERSION" = "1.2.x" ] + [ "$test_module_ai_requirement" != "$DP_AI_MODULE_VERSION" ] + # Script should fail in this case (verified separately) +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Use Case: No Test Module (Use DP_AI_MODULES with Compatibility Filtering) +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "DP_AI_MODULES: ai_search incompatible with AI 1.2.x → should skip" { + export DP_AI_MODULE_VERSION="1.2.x" + + eval "$(sed -n '/^is_compatible_with_ai()/,/^}/p' .devpanel/clone_ai_modules.sh)" + eval "$(sed -n '/^get_compatible_version()/,/^}/p' .devpanel/clone_ai_modules.sh)" + + # Check if ai_search is compatible with AI 1.2.x + run is_compatible_with_ai "repos/ai_search" "1.2.x" + + # Should be incompatible (exit 1) + [ "$status" -eq 1 ] + [[ "$output" =~ "Incompatible" ]] +} + +@test "DP_AI_MODULES: ai_provider_litellm compatible with AI 1.2.x → should include" { + export DP_AI_MODULE_VERSION="1.2.x" + + eval "$(sed -n '/^is_compatible_with_ai()/,/^}/p' .devpanel/clone_ai_modules.sh)" + eval "$(sed -n '/^get_compatible_version()/,/^}/p' .devpanel/clone_ai_modules.sh)" + + # Check if ai_provider_litellm is compatible with AI 1.2.x + run is_compatible_with_ai "repos/ai_provider_litellm" "1.2.x" + + # Should be compatible (exit 0) + [ "$status" -eq 0 ] +} + +@test "DP_AI_MODULES: ai_agents compatible with AI 1.2.x → should include" { + export DP_AI_MODULE_VERSION="1.2.x" + + eval "$(sed -n '/^is_compatible_with_ai()/,/^}/p' .devpanel/clone_ai_modules.sh)" + eval "$(sed -n '/^get_compatible_version()/,/^}/p' .devpanel/clone_ai_modules.sh)" + + # Check if ai_agents is compatible with AI 1.2.x + run is_compatible_with_ai "repos/ai_agents" "1.2.x" + + # Should be compatible (exit 0) + [ "$status" -eq 0 ] +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Use Case: Verify Correct Modules Get Added to COMPATIBLE_AI_MODULES +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "Compatibility filtering: AI 1.2.x should only include compatible modules" { + export DP_AI_MODULE_VERSION="1.2.x" + export COMPATIBLE_AI_MODULES="" + + eval "$(sed -n '/^is_compatible_with_ai()/,/^}/p' .devpanel/clone_ai_modules.sh)" + eval "$(sed -n '/^get_compatible_version()/,/^}/p' .devpanel/clone_ai_modules.sh)" + + # Simulate checking each module + modules="ai_provider_litellm ai_search ai_agents" + + for module in $modules; do + if is_compatible_with_ai "repos/$module" "1.2.x" 2>/dev/null; then + if [ -z "$COMPATIBLE_AI_MODULES" ]; then + COMPATIBLE_AI_MODULES="$module" + else + COMPATIBLE_AI_MODULES="$COMPATIBLE_AI_MODULES,$module" + fi + fi + done + + # Verify: should include ai_provider_litellm and ai_agents, but NOT ai_search + [[ "$COMPATIBLE_AI_MODULES" =~ "ai_provider_litellm" ]] + [[ "$COMPATIBLE_AI_MODULES" =~ "ai_agents" ]] + [[ ! "$COMPATIBLE_AI_MODULES" =~ "ai_search" ]] +} + +@test "Compatibility filtering: AI 2.0.x should include ai_search" { + export DP_AI_MODULE_VERSION="2.0.x" + export COMPATIBLE_AI_MODULES="" + + eval "$(sed -n '/^is_compatible_with_ai()/,/^}/p' .devpanel/clone_ai_modules.sh)" + eval "$(sed -n '/^get_compatible_version()/,/^}/p' .devpanel/clone_ai_modules.sh)" + + # Check if ai_search is compatible with AI 2.0.x + if is_compatible_with_ai "repos/ai_search" "2.0.x" 2>/dev/null; then + COMPATIBLE_AI_MODULES="ai_search" + fi + + # Verify: should include ai_search + [ "$COMPATIBLE_AI_MODULES" = "ai_search" ] +} + +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +# Use Case: Module Without composer.json or AI Dependency +# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + +@test "Module without composer.json is treated as compatible" { + mkdir -p repos/custom_module + # No composer.json + + eval "$(sed -n '/^is_compatible_with_ai()/,/^}/p' .devpanel/clone_ai_modules.sh)" + + run is_compatible_with_ai "repos/custom_module" "1.2.x" + + # Should be compatible (exit 0) + [ "$status" -eq 0 ] + + rm -rf repos/custom_module +} + +@test "Module without AI dependency is treated as compatible" { + mkdir -p repos/standalone_module + cat > repos/standalone_module/composer.json <<'EOF' +{"require": {"php": ">=8.1"}} +EOF + + eval "$(sed -n '/^is_compatible_with_ai()/,/^}/p' .devpanel/clone_ai_modules.sh)" + + run is_compatible_with_ai "repos/standalone_module" "1.2.x" + + # Should be compatible (exit 0) + [ "$status" -eq 0 ] + + rm -rf repos/standalone_module +}