logo

एक्स्ट्रा ब्लॉक टाइप्स (EBT) - नया लेआउट बिल्डर अनुभव❗

एक्स्ट्रा ब्लॉक टाइप्स (EBT) - स्टाइलिश, कस्टमाइज़ेबल ब्लॉक टाइप्स: स्लाइडशो, टैब्स, कार्ड्स, एकॉर्डियन्स और कई अन्य। बैकग्राउंड, DOM बॉक्स, जावास्क्रिप्ट प्लगइन्स के लिए बिल्ट-इन सेटिंग्स। आज ही लेआउट बिल्डिंग का भविष्य अनुभव करें।

डेमो EBT मॉड्यूल्स EBT मॉड्यूल्स डाउनलोड करें

❗एक्स्ट्रा पैराग्राफ टाइप्स (EPT) - नया पैराग्राफ्स अनुभव

एक्स्ट्रा पैराग्राफ टाइप्स (EPT) - एनालॉजिकल पैराग्राफ आधारित मॉड्यूल्स का सेट।

डेमो EPT मॉड्यूल्स EPT मॉड्यूल्स डाउनलोड करें

GLightbox is a pure javascript lightbox (Colorbox alternative without jQuery)❗

It can display images, iframes, inline content and videos with optional autoplay for YouTube, Vimeo and even self-hosted videos.

Demo GLightbox Download GLightbox

स्क्रॉल

Drupal CI‑आधारित कॉन्फ़िगरेशन मैनेजमेंट (Jenkins और GitLab CI का उपयोग करते हुए)

16/04/2026, by Ivan

1. CI-आधारित कॉन्फ़िगरेशन मैनेजमेंट क्यों महत्वपूर्ण है

Drupal की कॉन्फ़िगरेशन प्रणाली इस प्लेटफ़ॉर्म की सबसे बड़ी ताकतों में से एक है — और इसके साथ होने वाली समस्याओं का एक बहुत विश्वसनीय स्रोत भी। साइट की हर कॉन्फ़िगरेशन को YAML फ़ाइलों के रूप में export और import करने की क्षमता बहुत शक्तिशाली है, लेकिन तभी जब सभी सहमत हों कि इन फ़ाइलों को विभिन्न वातावरणों के बीच स्थानांतरित करने की ज़िम्मेदारी किसकी है। अधिकांश टीमों में, यह सहमति वास्तव में कभी बन ही नहीं पाती।

क्लासिक समस्याएँ किसी भी व्यक्ति के लिए अच्छी तरह जानी जाती हैं जिसने Drupal साइट डिलीवर की है:

  • Config drift — staging production से अलग हो जाता है, production local से अलग हो जाता है, और किसी को भी यह सुनिश्चित नहीं होता कि कौन सा environment canonical है।
  • "Staging पर काम करता है लेकिन prod पर नहीं" — क्योंकि किसी ने staging पर कोई view या field formatter अपडेट किया और उसे कभी export नहीं किया।
  • Manual drush cim से content टूट जाना — रात 11 बजे की जल्दी में किया गया एक import जिसने उस content type के field को हटा दिया जो अभी भी live nodes द्वारा referenced था।

इन सभी परिस्थितियों का मूल कारण एक ही है: एक इंसान यह तय कर रहा होता है कि कॉन्फ़िगरेशन कब और कैसे प्रमोट होगी। इंसान भूल जाते हैं। दबाव में वे कुछ स्टेप्स छोड़ देते हैं। वे ऐसे निर्णय लेते हैं जो बाद में गलत साबित होते हैं।

CI नहीं भूलता। एक pipeline या तो पास होती है या फेल। उसे किसी standup में शामिल होने की जल्दी नहीं होती। उसे यह नहीं पता कि release बीस मिनट में है। यही निर्धारण (determinism) कॉन्फ़िगरेशन मैनेजमेंट को चाहिए।

यह आर्टिकल जिन बातों का वादा करता है:

  • हर कॉन्फ़िगरेशन बदलाव किसी भी shared environment को छूने से पहले Git में commit किया जाता है।
  • कॉन्फ़िगरेशन को validate और import करने की ज़िम्मेदारी pipeline की होती है, न कि developer की।
  • environments के बीच promotion के लिए किसी भी manual स्टेप की आवश्यकता नहीं होती।
  • Config drift एक Slack मैसेज नहीं, बल्कि एक build failure होता है।
मान्यताएँ: आप Drupal 10 या 11 चला रहे हैं, feature branches के साथ Git-आधारित workflow का उपयोग कर रहे हैं, और कम से कम दो shared environments (जैसे staging और production) पर deploy कर रहे हैं। आपकी टीम Jenkins या GitLab CI — या दोनों — का उपयोग करती है।

2. वास्तविक प्रोजेक्ट्स से हमने जो मुख्य सिद्धांत सीखे

Configuration ही कोड है

यदि यह साइट के व्यवहार को बदलता है, तो इसे Git में होना चाहिए। बस। एक view, एक content type, एक performance setting, एक image style — यह सब कोड है। कॉन्फ़िग फ़ाइलों के साथ वही अनुशासन रखें जो PHP फ़ाइलों के साथ रखते हैं: उन्हें review करें, version करें, और shared environment में सीधे कभी edit न करें।

Shared environments में कोई manual drush cim नहीं

Pipeline कॉन्फ़िगरेशन import करती है। Developers नहीं। यह नियम तब तक अतिवादी लगता है जब तक कि कोई production पर drush cim चला देता है जबकि उसके working directory में uncommitted local changes मौजूद हों।

Pipelines को config drift पर तुरंत fail होना चाहिए

कॉन्फ़िगरेशन को import करने के बाद यदि तुरंत export करने पर कोई diff आता है, तो build fail होनी चाहिए। यह एक नियम उन सभी checks से अधिक bugs पकड़ता है जिन्हें हमने जोड़ा है।

एक senior developer production पर एक धीमे search page को debug कर रहा था। उसने UI के माध्यम से Search API index settings को tweak किया, performance में सुधार की पुष्टि की, और ticket बंद कर दिया। दो सप्ताह बाद, एक release ने Git से कॉन्फ़िगरेशन import की — जिससे उसके UI बदलाव overwrite हो गए — और search performance गिर गई। तीन दिनों तक किसी को भी कारण समझ नहीं आया। उस घटना के बाद हमने नियम स्पष्ट कर दिया: जो भी UI बदलाव तुरंत export और commit नहीं किया जाता, उसे खोया हुआ माना जाएगा।
एक media-heavy साइट पर, core.extension.yml को exports से बाहर रखा गया क्योंकि "modules तो Composer द्वारा वैसे भी manage होते हैं।" तीन महीने बाद एक hotfix ने उस module को फिर से enable कर दिया जिसे production में जानबूझकर disable किया गया था। Hotfix सही था — लेकिन बाद में हुए config import ने चुपचाप module को फिर से disable कर दिया। यही कारण है कि अब हम core.extension.yml को सबसे खतरनाक फ़ाइल कहते हैं जिसे आप अनदेखा कर सकते हैं।

3. उच्च-स्तरीय आर्किटेक्चर

👨‍💻 डेवलपर
drush cex
📁 Git रिपॉज़िटरी
config/sync
⚙ CI पाइपलाइन
validate + import
🌐 Dev
🌐 Stage
🌐 Prod

कॉन्फ़िगरेशन केवल एक दिशा में प्रवाहित होती है: डेवलपर के लोकल export से, Git के माध्यम से, CI पाइपलाइन के जरिए, और प्रत्येक environment तक।

मुख्य समझ separation of concerns (जिम्मेदारियों का विभाजन) है:

भूमिकाजिम्मेदारीकिसके लिए कभी जिम्मेदार नहीं
डेवलपरकॉन्फ़िग export करना, Git में commit करना, MR/PR खोलनाकिसी भी shared environment पर कॉन्फ़िग import करना
CI पाइपलाइनValidate करना, import करना, verify करना, promote करनाकॉन्फ़िग फ़ाइलों को जनरेट या एडिट करना
Environmentसाइट को चलानाकॉन्फ़िग को जनरेट, स्टोर या export करना

Environments कॉन्फ़िगरेशन के उपभोक्ता होते हैं, निर्माता नहीं। जिस क्षण कोई environment कॉन्फ़िग के लिए source of truth बन जाता है, drift अवश्यंभावी हो जाती है।

4. स्केलेबल रिपॉज़िटरी संरचना

Directory Tree project root
project-root/
├── composer.json
├── composer.lock
├── Jenkinsfile
├── .gitlab-ci.yml
│
├── ci/
│   ├── drupal-config-check.sh   # पुनः प्रयोज्य validation स्क्रिप्ट
│   ├── drupal-deploy.sh
│   └── drupal-install.sh
│
├── config/
│   ├── sync/                        # कॉन्फ़िग यहाँ रहती है — Git में committed
│   │   ├── core.extension.yml
│   │   ├── system.site.yml
│   │   └── ...
│   └── splits/                      # प्रति environment config_split overrides
│       ├── development/
│       ├── staging/
│       └── production/
│
└── web/
    ├── sites/
    │   └── default/
    │       ├── settings.php          # Committed, कोई secrets नहीं
    │       ├── settings.local.php    # Gitignored, लोकल overrides
    │       └── settings.env.php      # CI द्वारा env vars से लोडेड
    └── ...

config/sync में क्या होता है

drush cex द्वारा जनरेट किए गए सभी कॉन्फ़िगरेशन YAML फ़ाइलें। यह डायरेक्टरी साइट कॉन्फ़िगरेशन के लिए single source of truth है। इसे commit किया जाता है, review किया जाता है, और किसी भी अन्य कोड की तरह deploy किया जाता है।

कॉन्फ़िग में क्या कभी नहीं जाना चाहिए

  • Environment-specific hostnames, API keys, या credentials — environment variables और settings.env.php का उपयोग करें।
  • किसी भी प्रकार के secrets — अपने CI secret store (Jenkins Credentials या GitLab CI Variables) के माध्यम से inject करें।
  • Content — जो config होना चाहिए उसके लिए Default Content module का उपयोग न करें।
PHP web/sites/default/settings.php
// settings.php — Git में committed, environment-agnostic
$settings['config_sync_directory'] = DRUPAL_ROOT . '/../config/sync';

// CI या host द्वारा inject किए गए environment-specific values लोड करें।
if (file_exists($app_root . '/' . $site_path . '/settings.env.php')) {
  include $app_root . '/' . $site_path . '/settings.env.php';
}

// वैकल्पिक लोकल overrides लोड करें (gitignored)।
if (file_exists($app_root . '/' . $site_path . '/settings.local.php')) {
  include $app_root . '/' . $site_path . '/settings.local.php';
}
PHP web/sites/default/settings.env.php
// CI द्वारा environment variables से जनरेट — कभी commit न करें।
$databases['default']['default'] = [
  'driver'   => 'mysql',
  'host'     => getenv('DB_HOST'),
  'database' => getenv('DB_NAME'),
  'username' => getenv('DB_USER'),
  'password' => getenv('DB_PASS'),
  'port'     => getenv('DB_PORT') ?: 3306,
  'prefix'   => '',
];

$settings['hash_salt'] = getenv('DRUPAL_HASH_SALT');

// config_split को बताएं कि हम किस environment में हैं।
$config['config_split.config_split.production']['status'] =
  (getenv('APP_ENV') === 'production');

5. Drupal कॉन्फ़िगरेशन वर्कफ़्लो

5.1 लोकल डेवलपमेंट

लोकल वर्कफ़्लो ही एकमात्र जगह है जहाँ UI बदलावों और कॉन्फ़िग export के बीच feedback loop तेज़ और आदत का हिस्सा होना चाहिए। प्रोजेक्ट में प्रत्येक डेवलपर एक जैसे नियमों का पालन करता है:

  • लोकली UI या कोड बदलाव करें।
  • तुरंत drush cex चलाएँ — दिन के अंत में नहीं।
  • git diff config/sync के साथ diff की समीक्षा करें।
  • या तो बदलाव commit करें या git checkout -- config/sync के साथ उन्हें discard करें।
  • बीच की कोई स्थिति नहीं है।
Shell लोकल डेवलपमेंट वर्कफ़्लो
# Drupal UI या install hooks के माध्यम से कॉन्फ़िग बदलाव करने के बाद:
drush cex --yes

# क्या बदला है इसकी समीक्षा करें — इसे किसी भी कोड बदलाव की तरह देखें:
git diff config/sync

# फीचर कोड के साथ stage और commit करें:
git add config/sync
git commit -m "feat(search): add fulltext search API index config"

# या यदि बदलाव परीक्षणात्मक था और अभी तैयार नहीं है, तो discard करें:
git checkout -- config/sync

हम इसे एक हल्के Git pre-commit hook के माध्यम से लागू करते हैं जो चेतावनी देता है (लेकिन block नहीं करता) जब PHP या template फ़ाइलें stage की जाती हैं बिना config/sync में संबंधित बदलाव के:

Shell .git/hooks/pre-commit
#!/bin/bash
# यदि module/theme कोड बदला है लेकिन config/sync नहीं बदला है तो चेतावनी दें।
CHANGED_CODE=$(git diff --cached --name-only | grep -E '\.(php|module|theme|install)$')
CHANGED_CONFIG=$(git diff --cached --name-only | grep '^config/sync')

if [[ -n "$CHANGED_CODE" ]] && [[ -z "$CHANGED_CONFIG" ]]; then
  echo "⚠  चेतावनी: PHP/module फ़ाइलें stage की गई हैं लेकिन config/sync बदलाव नहीं मिला।"
  echo "   क्या आप drush cex चलाना भूल गए?"
  echo "   फिर भी आगे बढ़ रहे हैं — लेकिन दोबारा जाँच लें।"
fi
exit 0

5.2 फीचर ब्रांचेस

कॉन्फ़िग उसी ब्रांच में और उसी pull/merge request में उस कोड के साथ रहती है जिसे उसकी आवश्यकता होती है। एक नया content type जोड़ने वाली feature में उसी commit में PHP install hook (यदि कोई हो) और उस content type के YAML फ़ाइलें शामिल होनी चाहिए।

टूटी हुई dependencies को जल्दी पकड़ना: यदि branch A एक नया field जोड़ती है और branch B उसी field के display को बदल देती है, तो A से पहले B को merge करने पर config import error उत्पन्न होगा। यह सही परिणाम है — आप चाहते हैं कि pipeline इसे branch पर ही पकड़ ले, production पर नहीं।

6. CI की जिम्मेदारियाँ: पाइपलाइन को क्या लागू करना चाहिए

प्रत्येक पाइपलाइन जो किसी shared Drupal environment को प्रभावित करती है, उसे निम्न चरणों को क्रम में चलाना चाहिए:

  1. डेटाबेस install या restore करें — एक clean install या sanitized production snapshot।
  2. डेटाबेस अपडेट्स चलाएँdrush updb
  3. कॉन्फ़िगरेशन import करेंdrush cim --yes
  4. कॉन्फ़िगरेशन को पुनः export करेंdrush cex --yes
  5. कोई diff नहीं है यह सुनिश्चित करें — यदि कोई YAML फ़ाइल बदली है, तो build fail कर दें।

चरण 4 और 5 मिलकर सबसे महत्वपूर्ण हैं। Import करने के बाद तुरंत re-export करने पर खाली diff आना चाहिए। यदि नहीं आता, तो इनमें से एक बात सही हो सकती है:

  • कोई module import के दौरान कॉन्फ़िग जनरेट कर रहा है (अक्सर module में bug होता है)।
  • किसी config entity का UUID डेटाबेस से मेल नहीं खाता।
  • कोई config schema अधूरा है, जिससे Drupal values को export किए गए तरीके से अलग तरह normalize करता है।
  • किसी डेवलपर ने config फ़ाइलों को हाथ से संपादित कर दिया और असंगतियाँ पैदा कर दीं।
Shell ci/drupal-config-check.sh
#!/bin/bash
set -euo pipefail

echo "=== Drupal डेटाबेस अपडेट्स चलाए जा रहे हैं ==="
drush updb --yes

echo "=== config/sync से कॉन्फ़िगरेशन import किया जा रहा है ==="
drush cim --yes

echo "=== यह सत्यापित करने के लिए पुनः export किया जा रहा है कि कोई लंबित बदलाव नहीं है ==="
drush cex --yes

echo "=== कॉन्फ़िग drift की जाँच की जा रही है ==="
if ! git diff --exit-code config/sync; then
  echo ""
  echo "❌ FAIL: कॉन्फ़िगरेशन drift का पता चला!"
  echo "   Git में मौजूद कॉन्फ़िग Drupal द्वारा import+export के बाद उत्पन्न कॉन्फ़िग से मेल नहीं खाता।"
  echo "   ऊपर diff दिखाया गया है। इसे लोकली 'drush cex' के साथ ठीक करें और commit करें।"
  exit 1
fi

echo "✅ कॉन्फ़िगरेशन साफ़ है — कोई drift नहीं मिला।"
Import के बाद re-export करना अनिवार्य क्यों है: Re-export चरण के बिना, आपको केवल यह पता चलता है कि import सफल हुआ। आपको यह नहीं पता चलता कि imported कॉन्फ़िग Drupal द्वारा आंतरिक रूप से स्टोर किए गए कॉन्फ़िग से मेल खाता है या नहीं। केवल re-export और diff करने से यह अंतर समाप्त होता है।
 

7. Jenkins इम्प्लीमेंटेशन (Battle‑Tested)

7.1 Jenkinsfile संरचना

हम विशेष रूप से declarative pipelines का उपयोग करते हैं। Scripted pipelines अधिक लचीलापन प्रदान करते हैं, लेकिन declarative pipelines पढ़ने में आसान होती हैं, jenkins-cli declarative-linter के साथ lint करना आसान होता है, और नए टीम सदस्यों के लिए समझना भी आसान होता है।

pipeline {
  agent { label 'drupal-php82' }

  options {
    buildDiscarder(logRotator(numToKeepStr: '20'))
    timeout(time: 30, unit: 'MINUTES')
    disableConcurrentBuilds()
  }

  environment {
    DRUPAL_ROOT       = "${WORKSPACE}/web"
    COMPOSER_HOME     = "${WORKSPACE}/.composer"
    APP_ENV           = 'ci'
    DB_CREDS          = credentials('drupal-ci-db') // Jenkins secret
  }

  stages {

    stage('Checkout') {
      steps {
        checkout scm
        sh 'git log --oneline -5'
      }
    }

    stage('Composer Install') {
      steps {
        sh '''
          composer install \
            --no-interaction \
            --prefer-dist \
            --optimize-autoloader
        '''
      }
    }

    stage('Write Settings') {
      steps {
        // Jenkins credentials/env vars से settings.env.php जनरेट करें
        sh '''
          cat > web/sites/default/settings.env.php <<EOF
\$databases['default']['default'] = [
  'driver'   => 'mysql',
  'host'     => '127.0.0.1',
  'database' => 'drupal_ci',
  'username' => '${DB_CREDS_USR}',
  'password' => '${DB_CREDS_PSW}',
  'port'     => 3306,
  'prefix'   => '',
];
\$settings['hash_salt'] = '${DRUPAL_HASH_SALT}';
EOF
        '''
      }
    }

    stage('Site Install') {
      steps {
        sh '''
          drush site-install minimal \
            --yes \
            --existing-config \
            --account-name=admin \
            --account-pass="${DRUPAL_ADMIN_PASS}"
        '''
      }
    }

    stage('Validate Config') {
      steps {
        sh 'bash ci/drupal-config-check.sh'
      }
      post {
        failure {
          sh 'git diff config/sync || true'
          archiveArtifacts artifacts: 'config/sync/**/*.yml', allowEmptyArchive: true
        }
      }
    }

    stage('Deploy') {
      when {
        anyOf {
          branch 'main'
          branch 'release/*'
        }
      }
      steps {
        sh 'bash ci/drupal-deploy.sh'
      }
    }

  }

  post {
    always {
      sh 'drush cr || true'
      cleanWs()
    }
    failure {
      emailext(
        subject: "[FAIL] ${JOB_NAME} #${BUILD_NUMBER}",
        body: "Config validation failed. See: ${BUILD_URL}",
        recipientProviders: [[$class: 'DevelopersRecipientProvider']]
      )
    }
  }
}

7.2 Jenkins‑विशिष्ट विचार

Shared Libraries

जब आप दो या तीन से अधिक Drupal साइट्स प्रबंधित कर रहे हों, तो Drupal pipeline लॉजिक को Jenkins Shared Library में निकाल लें। तब प्रत्येक प्रोजेक्ट का Jenkinsfile एक पतला wrapper बन जाता है:

Groovy Jenkinsfile (shared library का उपयोग करने वाला thin wrapper)
@Library('drupal-pipeline-lib@v2') _

drupalPipeline(
  phpVersion:    '8.2',
  deployBranch:  'main',
  dbCredentials: 'drupal-ci-db',
  slackChannel:  '#deployments'
)

Workspace Cleanup

हमेशा post { always } ब्लॉक में cleanWs() कॉल करें। Jenkins agents पिछले builds से state जमा करते रहते हैं — किसी पिछले build का settings.env.php या vendor directory चुपचाप वर्तमान build को प्रभावित कर सकता है। सख्त रहें: workspace साफ करें।

Pipeline runs के बीच कभी भी database reuse न करें जब तक कि आप स्पष्ट रूप से upgrade paths का परीक्षण नहीं कर रहे हों। पिछले build से बचा हुआ database UUID mismatches, missing migrations, और module install order समस्याओं को छिपा सकता है।

8. GitLab CI इम्प्लीमेंटेशन

8.1 .gitlab-ci.yml संरचना

YAML .gitlab-ci.yml
image: php:8.2-cli

stages:
  - build
  - test
  - validate-config
  - deploy

variables:
  COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/.cache/composer"
  APP_ENV: "ci"
  MYSQL_DATABASE: "drupal_ci"
  MYSQL_ROOT_PASSWORD: "root"

composer:install:
  stage: build
  script:
    - composer install --no-interaction --prefer-dist --optimize-autoloader
  artifacts:
    paths:
      - vendor/
      - web/core/
      - web/modules/contrib/
    expire_in: 1 hour

phpunit:unit:
  stage: test
  script:
    - ./vendor/bin/phpunit --testsuite=unit --log-junit=reports/phpunit.xml
  artifacts:
    reports:
      junit: reports/phpunit.xml

drupal:validate-config:
  stage: validate-config
  services:
    - name: mysql:8.0
      alias: mysql
  variables:
    DB_HOST: mysql
    DB_NAME: $MYSQL_DATABASE
    DB_USER: root
    DB_PASS: $MYSQL_ROOT_PASSWORD
  before_script:
    - bash ci/write-settings-env.sh
    - drush site-install minimal --yes --existing-config
  script:
    - bash ci/drupal-config-check.sh
  artifacts:
    when: on_failure
    paths:
      - config/sync/
    expire_in: 3 days

deploy:staging:
  stage: deploy
  environment:
    name: staging
    url: https://staging.example.com
  rules:
    - if: '$CI_COMMIT_BRANCH == "develop"'
  script:
    - bash ci/drupal-deploy.sh staging

deploy:production:
  stage: deploy
  environment:
    name: production
    url: https://example.com
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'
      when: manual
  script:
    - bash ci/drupal-deploy.sh production

8.2 GitLab CI के लाभ

GitLab CI में कई सुविधाएँ हैं जो इस वर्कफ़्लो के लिए इसे विशेष रूप से साफ और प्रभावी बनाती हैं:

  • First-class artifacts — विफल कॉन्फ़िग exports स्वतः संग्रहीत हो जाते हैं और MR UI में किसी अतिरिक्त कॉन्फ़िगरेशन के बिना देखे जा सकते हैं।
  • composer.lock पर आधारित native caching — पहले run के बाद Composer installs तेज़ हो जाते हैं।
  • Merge Request pipeline visibility — डेवलपर्स merge से पहले MR पर सीधे कॉन्फ़िग validation की स्थिति देख सकते हैं।
  • Services block — MySQL zero infrastructure overhead के साथ sidecar के रूप में शुरू हो जाता है।
  • Environments + manual gates — production deploy पर when: manual नियम one‑click promotion की सुविधा देता है जिसमें मानव अनुमोदन भी शामिल होता है।
Shell ci/drupal-deploy.sh
#!/bin/bash
set -euo pipefail

TARGET_ENV="${1:-staging}"
echo "=== Deploying to: ${TARGET_ENV} ==="

rsync -az --delete \
  --exclude='.git' \
  --exclude='web/sites/default/files' \
  --exclude='web/sites/default/settings.env.php' \
  ./ "deploy@${TARGET_ENV}.example.com:/var/www/drupal/"

echo "=== Running post-deploy commands on ${TARGET_ENV} ==="
ssh "deploy@${TARGET_ENV}.example.com" bash -s <<'REMOTE'
  set -e
  cd /var/www/drupal
  drush updb --yes
  drush cim --yes
  drush cex --yes
  git diff --exit-code config/sync || (echo "❌ Config drift on target!"; exit 1)
  drush cr
  echo "✅ Deploy complete."
REMOTE

9. बिना समझौता किए Environment‑Specific कॉन्फ़िगरेशन

प्रत्येक कॉन्फ़िगरेशन मान सभी environments में समान नहीं होना चाहिए। Search backends, logging verbosity, caching layers, और third‑party API endpoints जैसे तत्व वैध रूप से अलग होते हैं। प्रश्न यह है कि उस अंतर को कैसे संभाला जाए बिना आपके config pipeline की अखंडता से समझौता किए।

सही टूल्स: config_split और config_ignore

config_split आपको ऐसे कॉन्फ़िगरेशन सेट्स परिभाषित करने की अनुमति देता है जो केवल विशेष environments में सक्रिय होते हैं। प्रत्येक split अपनी अलग directory में रहता है और environment variable के आधार पर settings.php या settings.env.php के माध्यम से सक्रिय किया जाता है।

YAML config/sync/config_split.config_split.development.yml
langcode: en
status: true
id: development
label: Development
description: 'केवल local/dev environments पर सक्रिय कॉन्फ़िग'
folder: '../config/splits/development'
module:
  devel: 0
  kint: 0
  dblog: 0
theme: {  }
blacklist: {  }
graylist: {  }
PHP web/sites/default/settings.env.php (environment activation)
$app_env = getenv('APP_ENV') ?: 'production';

// Environment के आधार पर सही config_split सक्रिय करें।
$config['config_split.config_split.development']['status']  = ($app_env === 'development');
$config['config_split.config_split.staging']['status']     = ($app_env === 'staging');
$config['config_split.config_split.production']['status']   = ($app_env === 'production');

वास्तविक दुनिया के split उदाहरण

कॉन्फ़िग आइटमDev splitStage splitProd split
Search API backendDatabase backendSolr (small)Solr (prod cluster)
Error loggingdblog, verbosesyslogsyslog + external APM
Performance modulesDisabledEnabledEnabled + CDN config
Mail transportMailhog / null mailerMailpitSMTP / SendGrid

गलत तरीके — इन्हें न करें:

  • Production डेटाबेस पर सीधे कॉन्फ़िगरेशन को संपादित करना।
  • settings.php के अंदर if ($settings['environment'] === 'prod') जैसी conditionals का उपयोग करके कॉन्फ़िग मान बदलना — यह config system को पूरी तरह bypass करता है और runtime divergence पैदा करता है।
  • प्रत्येक environment के लिए अलग Git branches में अलग‑अलग कॉन्फ़िग फ़ाइलों को रखना।

10. Environments के बीच Promotion

Promotion deployment नहीं है। Deployment कोड और कॉन्फ़िग को किसी environment में ले जाता है। Promotion एक validated artifact को — जो पहले ही CI पास कर चुका हो — चैन में अगले environment तक ले जाता है।

व्यवहार में, इसका मतलब है कि वही Git SHA जो dev पर validate हुआ था, वही production तक पहुँचता है। कोई last‑minute commits नहीं। कोई ऐसे hotfixes नहीं जो CI को skip करें। कोई cherry‑picks नहीं जो config check को bypass करें।

✅ CI SHA को Validate करता है
feature branch / main पर
🌐 Dev
merge पर auto‑deploy
🌐 Stage
develop से auto‑deploy
🌐 Prod
main पर manual gate

वही validated artifact environments के माध्यम से आगे बढ़ता है। CI एक बार चलता है; परिणाम आगे propagate होता है।

Immutable builds दर्शन का अर्थ है: CI द्वारा validate किया गया ही deploy किया जाएगा। यदि वास्तव में hotfix आवश्यक है, तो उसे अपने स्वयं के CI run के साथ fast‑track branch के माध्यम से जाना होगा — वह validation को bypass नहीं करेगा।

Last‑minute hotfixes को config checks छोड़ने से रोकना: अपने main branch को required pipeline status check के साथ सुरक्षित करें। GitLab के "protected branches" और Jenkins के "GitHub Branch Source" plugin दोनों इसका समर्थन करते हैं। यदि pipeline पास नहीं हुआ है, तो branch को production में deploy नहीं किया जा सकता।

11. Edge Cases को संभालना (आप इनसे टकराएँगे ही)

UUID Mismatches

Config entities अपने साथ UUIDs लेकर चलते हैं। यदि आप एक नई साइट install करते हैं और फिर किसी अन्य installation से config import करने का प्रयास करते हैं, तो Drupal UUID mismatch error के साथ मना कर देगा। समाधान यह है कि हमेशा --existing-config के साथ install करें या install के बाद site UUID सेट करें:

Shell
# Committed config से UUID पढ़ें और fresh install पर लागू करें:
SITE_UUID=$(grep "^uuid:" config/sync/system.site.yml | awk '{print $2}')
drush config-set "system.site" uuid "$SITE_UUID" --yes
drush cim --yes

Module Enable/Disable Order

जब किसी नए module को config के माध्यम से enable किया जाता है, तो Drupal dependency order का स्वतः सम्मान करता है जब drush cim चलाया जाता है। हालांकि, यदि किसी module का install hook default config जनरेट करता है जो config/sync में मौजूद config से टकराता है, तो आपको उस default config को हटाना और दोबारा import करना पड़ सकता है। Pipeline इसे drift के रूप में पकड़ लेगा।

Content‑Dependent Configuration

कुछ config content को reference करती हैं — उदाहरण के लिए, एक block जो किसी menu item को ID के आधार पर reference करता है, या एक view जो किसी taxonomy term के अनुसार filter करता है। यदि content विभिन्न environments में अलग तरीके से बनाया गया है तो ये references अलग हो सकते हैं। समाधान यह है कि उस content को migrations या default content modules के माध्यम से manage करें, न कि config के माध्यम से।

Multisite Quirks

Multisite setup में प्रत्येक site की अपनी config sync directory होती है। CI pipeline को प्रत्येक site के लिए config check चलाना चाहिए, सिर्फ primary site के लिए नहीं। इसके लिए loop का उपयोग करें:

Shellci/multisite-config-check.sh
#!/bin/bash
set -euo pipefail

for SITE_DIR in web/sites/*/; do
  SITE=$(basename "$SITE_DIR")
  [[ "$SITE" == "default" ]] && continue
  [[ "$SITE" == "simpletest" ]] && continue

  echo "--- Checking config for site: $SITE ---"
  drush --uri="${SITE}" updb --yes
  drush --uri="${SITE}" cim --yes
  drush --uri="${SITE}" cex --yes

  CONFIG_DIR="config/${SITE}/sync"
  if ! git diff --exit-code "${CONFIG_DIR}"; then
    echo "❌ Config drift in site: $SITE"
    exit 1
  fi
done
echo "✅ All sites clean."

Drupal Core को Config Changes के साथ Upgrade करना

Core updates कभी‑कभी schema changes के साथ आते हैं जो मौजूदा config को प्रभावित करते हैं। हमेशा drush cim से पहले drush updb चलाएँ, और उसके बाद drush cex चलाकर किसी भी schema‑normalised बदलाव को कैप्चर करें। उन बदलावों को core upgrade branch के हिस्से के रूप में commit करें — pipeline में उनसे आश्चर्यचकित न हों।

12. ऑपरेशनल Safety Nets

Production में Read‑Only Config

Production में Config Readonly module को enable करने पर विचार करें। यह admin UI के माध्यम से किसी भी configuration change को रोकता है — एक कठोर अवरोध जो application स्तर पर "no manual changes" नियम को मजबूत करता है।

PHPweb/sites/default/settings.env.php
if (getenv('APP_ENV') === 'production') {
  $settings['config_readonly'] = TRUE;
}

Post‑Deploy Verification

ShellPost-deploy checks
# Deploy के बाद कोई लंबित config बदलाव शेष नहीं हैं यह सत्यापित करें:
drush config-status 2>&1 | grep -v 'No differences' && { \
  echo "❌ Deploy के बाद अप्रत्याशित config अंतर"; exit 1; \
} || echo "✅ Config status clean"

# Verify करें कि caches warm हैं:
drush cr
drush php-eval "echo \Drupal::state()->get('system.cron_last');"

Rollback Strategy

Rollback एक Git operation है, database operation नहीं। यदि deployment किसी समस्या का कारण बनता है:

  1. अंतिम सही commit SHA की पहचान करें।
  2. उसी SHA पर pipeline run ट्रिगर करें।
  3. Pipeline पहले से validated artifact को पुनः deploy कर देगा।

Config import को undo करने के लिए manual database surgery कभी भी समाधान नहीं है — यह सभी safety checks को bypass करता है और आमतौर पर जितना ठीक करता है उससे अधिक drift उत्पन्न कर देता है।

Audit करना: किसने Config बदला, कब, और क्यों

क्योंकि हर config बदलाव Git के माध्यम से जाता है, आपका audit trail आपकी Git history होती है। अर्थपूर्ण commit messages का उपयोग करें और उन्हें commit‑msg hook या CI lint step के माध्यम से लागू करें:

ShellExample commit history
$ git log --oneline config/sync/views.view.articles.yml

a3f8c12 feat(views): articles view में taxonomy filter जोड़ें (PROJ-421)
9e1b307 fix(views): query timeout का कारण बनने वाली exposed sort हटाएँ (PROJ-389)
4d22a91 chore(config): Search API Solr 4.3.0 upgrade के बाद export

13. सामान्य गलतियाँ जो अब हम नहीं करते

गलतीयह क्यों नुकसानदायक हैसमाधान
staging या production पर manually drush cim चलानाएक अज्ञात स्थिति import हो जाती है — जिसमें uncommitted local changes भी शामिल हो सकते हैंShared environments पर केवल pipeline ही config import करेगी
Staging पर UI बदलाव की अनुमति देनाStaging Git से अलग होकर source of truth बन जाता हैसभी shared environments पर config_readonly enable करें
Developers पर "export करना याद रखने" का भरोसा करनादबाव में वे ऐसा नहीं करेंगेPre-commit hooks चेतावनी देते हैं; CI failure इसे लागू करता है
Config drift होने पर build fail न करनाSilent divergence जमा होती रहती है जब तक कुछ बड़ा न टूट जाएdrush cim → cex → git diff --exit-code प्रक्रिया अनिवार्य है
प्रत्येक environment के लिए अलग branch के साथ अलग config रखनाMerge करना conflict nightmare बन जाता है; कोई single source of truth नहीं रहताConfig की एक branch रखें, जिसे config_split द्वारा विभाजित किया जाए
drush cim से पहले drush updb को skip करनाSchema updates import failures या silent data loss का कारण बन सकते हैंहमेशा: updb → cim → cex → diff

14. इस पैटर्न को लागू करने के बाद प्राप्त परिणाम

इस दृष्टिकोण को विभिन्न Drupal साइट्स के पोर्टफोलियो में लागू करने के बाद — एक छोटे एकल साइट से लेकर दर्जनों installations को प्रबंधित करने वाली एजेंसी तक — परिणाम लगातार एक जैसे रहे हैं:

~80%
Config‑संबंधित deployment incidents में कमी
< 1 दिन
नए डेवलपर का config workflow में onboarding समय
0
18 महीनों में production पर manual drush cim runs
100%
Git commit और MR से traceable config बदलाव

संख्याओं से परे, सांस्कृतिक परिवर्तन सबसे महत्वपूर्ण है। Config से संबंधित चर्चाएँ Slack से Git history में स्थानांतरित हो जाती हैं। "क्या किसी ने staging पर image styles बदले?" के बजाय प्रश्न यह हो जाता है "क्या इसके लिए कोई commit है?" — और हमेशा होता है, अन्यथा बदलाव हुआ ही नहीं।

नए डेवलपर्स को अब यह जानने की आवश्यकता नहीं होती कि "इस समय कौन सा environment canonical है"। उत्तर हमेशा Git होता है। Pipeline इसे लागू करती है।

15. अंतिम सिफारिशें

  • कठोर शुरुआत करें, केवल उचित कारणों से ही नियमों में ढील दें। जो नियम अनावश्यक साबित हो उसे ढीला करना आसान है, लेकिन जो कभी था ही नहीं उसे सख्त करना कठिन।
  • हर config diff को build failure मानें। कोई अपवाद नहीं, कोई "release के बाद ठीक कर लेंगे" नहीं।
  • क्या deploy होगा इसका source of truth CI है — न कि डेवलपर का लैपटॉप, न staging environment, न Slack पर लिया गया निर्णय।
  • updb → cim → cex → diff अनुक्रम अटूट है। यदि आप कोई भी चरण छोड़ते हैं, तो आप बिना देखे उड़ रहे हैं।
  • वास्तविक environment अंतर के लिए config_split का उपयोग करें। Shortcut के रूप में settings.php conditionals का उपयोग न करें।
  • सभी shared environments पर config_readonly enable करें। आकस्मिक UI बदलावों को केवल हतोत्साहित न करें, बल्कि असंभव बना दें।
  • विफल config exports को CI artifacts के रूप में संग्रहीत करें। डेवलपर्स को यह देखना चाहिए कि वास्तव में क्या drift हुआ है, न कि केवल यह कि diff मौजूद है।
  • Rollback Git के माध्यम से करें, database surgery के द्वारा कभी नहीं। Pipeline दोनों दिशाओं में सबसे सुरक्षित मार्ग है।
यह दृष्टिकोण स्केलेबल है। चाहे आप एक साइट पर काम करने वाले एकल डेवलपर हों या पचास Drupal installations को प्रबंधित करने वाली एजेंसी, नियम समान रहते हैं। Pipeline को टीम के आकार या release के दबाव से कोई फर्क नहीं पड़ता। यह या तो पास होती है या फेल। यही स्थिरता इसका मुख्य उद्देश्य है।

Jenkins और GitLab CI दोनों ही इस अनुशासन को लागू करने में समान रूप से सक्षम हैं। GitLab CI को कम infrastructure की आवश्यकता होती है और इसमें बेहतर native artifact support होता है; जबकि Jenkins complex enterprise environments के लिए अधिक लचीलापन प्रदान करता है और कई projects में pipelines को standardise करने हेतु उत्कृष्ट shared library support देता है। अपनी संस्था के अनुरूप tool चुनें — इस लेख के सिद्धांत हर स्थिति में लागू होते हैं।

उद्देश्य कभी भी configuration management को जटिल बनाना नहीं था। उद्देश्य इसे उबाऊ बनाना है: एक नियमित प्रक्रिया जो हमेशा काम करे, जिसके बारे में किसी को सोचना न पड़े, और जो रात 11 बजे किसी incident का कारण न बने। एक सही तरीके से लागू की गई CI pipeline के साथ, आपको वही "उबाऊ" स्थिरता मिलती है।

Technical and architectural inquiries
Ivan Abramenko, Principal Drupal Architect
ivan.abramenko@drupalbook.org
Project inquiries
projects@drupalbook.org