Drupal CI‑आधारित कॉन्फ़िगरेशन मैनेजमेंट (Jenkins और GitLab CI का उपयोग करते हुए)
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 होता है।
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 पकड़ता है जिन्हें हमने जोड़ा है।
core.extension.yml को exports से बाहर रखा गया क्योंकि "modules तो Composer द्वारा वैसे भी manage होते हैं।" तीन महीने बाद एक hotfix ने उस module को फिर से enable कर दिया जिसे production में जानबूझकर disable किया गया था। Hotfix सही था — लेकिन बाद में हुए config import ने चुपचाप module को फिर से disable कर दिया। यही कारण है कि अब हम core.extension.yml को सबसे खतरनाक फ़ाइल कहते हैं जिसे आप अनदेखा कर सकते हैं।3. उच्च-स्तरीय आर्किटेक्चर
drush cex
config/sync
validate + import
कॉन्फ़िगरेशन केवल एक दिशा में प्रवाहित होती है: डेवलपर के लोकल 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. स्केलेबल रिपॉज़िटरी संरचना
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 का उपयोग न करें।
// 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';
}// 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 करें। - बीच की कोई स्थिति नहीं है।
# 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 में संबंधित बदलाव के:
#!/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 05.2 फीचर ब्रांचेस
कॉन्फ़िग उसी ब्रांच में और उसी pull/merge request में उस कोड के साथ रहती है जिसे उसकी आवश्यकता होती है। एक नया content type जोड़ने वाली feature में उसी commit में PHP install hook (यदि कोई हो) और उस content type के YAML फ़ाइलें शामिल होनी चाहिए।
6. CI की जिम्मेदारियाँ: पाइपलाइन को क्या लागू करना चाहिए
प्रत्येक पाइपलाइन जो किसी shared Drupal environment को प्रभावित करती है, उसे निम्न चरणों को क्रम में चलाना चाहिए:
- डेटाबेस install या restore करें — एक clean install या sanitized production snapshot।
- डेटाबेस अपडेट्स चलाएँ —
drush updb। - कॉन्फ़िगरेशन import करें —
drush cim --yes। - कॉन्फ़िगरेशन को पुनः export करें —
drush cex --yes। - कोई diff नहीं है यह सुनिश्चित करें — यदि कोई YAML फ़ाइल बदली है, तो build fail कर दें।
चरण 4 और 5 मिलकर सबसे महत्वपूर्ण हैं। Import करने के बाद तुरंत re-export करने पर खाली diff आना चाहिए। यदि नहीं आता, तो इनमें से एक बात सही हो सकती है:
- कोई module import के दौरान कॉन्फ़िग जनरेट कर रहा है (अक्सर module में bug होता है)।
- किसी config entity का UUID डेटाबेस से मेल नहीं खाता।
- कोई config schema अधूरा है, जिससे Drupal values को export किए गए तरीके से अलग तरह normalize करता है।
- किसी डेवलपर ने config फ़ाइलों को हाथ से संपादित कर दिया और असंगतियाँ पैदा कर दीं।
#!/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 नहीं मिला।"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 बन जाता है:
@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 साफ करें।
8. GitLab CI इम्प्लीमेंटेशन
8.1 .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 production8.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 की सुविधा देता है जिसमें मानव अनुमोदन भी शामिल होता है।
#!/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."
REMOTE9. बिना समझौता किए 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 के माध्यम से सक्रिय किया जाता है।
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: { }$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 split | Stage split | Prod split |
|---|---|---|---|
| Search API backend | Database backend | Solr (small) | Solr (prod cluster) |
| Error logging | dblog, verbose | syslog | syslog + external APM |
| Performance modules | Disabled | Enabled | Enabled + CDN config |
| Mail transport | Mailhog / null mailer | Mailpit | SMTP / 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 करें।
feature branch / main पर
merge पर auto‑deploy
develop से auto‑deploy
main पर manual gate
वही validated artifact environments के माध्यम से आगे बढ़ता है। CI एक बार चलता है; परिणाम आगे propagate होता है।
Immutable builds दर्शन का अर्थ है: CI द्वारा validate किया गया ही deploy किया जाएगा। यदि वास्तव में hotfix आवश्यक है, तो उसे अपने स्वयं के CI run के साथ fast‑track branch के माध्यम से जाना होगा — वह validation को bypass नहीं करेगा।
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 सेट करें:
# 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 --yesModule 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 का उपयोग करें:
#!/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" नियम को मजबूत करता है।
if (getenv('APP_ENV') === 'production') {
$settings['config_readonly'] = TRUE;
}Post‑Deploy Verification
# 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 किसी समस्या का कारण बनता है:
- अंतिम सही commit SHA की पहचान करें।
- उसी SHA पर pipeline run ट्रिगर करें।
- 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 के माध्यम से लागू करें:
$ 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 के बाद export13. सामान्य गलतियाँ जो अब हम नहीं करते
| गलती | यह क्यों नुकसानदायक है | समाधान |
|---|---|---|
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 को प्रबंधित करने वाली एजेंसी तक — परिणाम लगातार एक जैसे रहे हैं:
drush cim runsसंख्याओं से परे, सांस्कृतिक परिवर्तन सबसे महत्वपूर्ण है। 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.phpconditionals का उपयोग न करें। - सभी shared environments पर config_readonly enable करें। आकस्मिक UI बदलावों को केवल हतोत्साहित न करें, बल्कि असंभव बना दें।
- विफल config exports को CI artifacts के रूप में संग्रहीत करें। डेवलपर्स को यह देखना चाहिए कि वास्तव में क्या drift हुआ है, न कि केवल यह कि diff मौजूद है।
- Rollback Git के माध्यम से करें, database surgery के द्वारा कभी नहीं। Pipeline दोनों दिशाओं में सबसे सुरक्षित मार्ग है।
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 के साथ, आपको वही "उबाऊ" स्थिरता मिलती है।
Ivan Abramenko, Principal Drupal Architect
ivan.abramenko@drupalbook.org
projects@drupalbook.org