Bazel में लॉकफ़ाइल की सुविधा, किसी प्रोजेक्ट के लिए ज़रूरी सॉफ़्टवेयर लाइब्रेरी या पैकेज के खास वर्शन या डिपेंडेंसी को रिकॉर्ड करने की सुविधा देती है. यह ऐसा, मॉड्यूल रिज़ॉल्यूशन और एक्सटेंशन के आकलन के नतीजे को सेव करके करता है. लॉकफ़ाइल, फिर से बनाए जा सकने वाले बिल्ड को बढ़ावा देती है. इससे, डेवलपमेंट के माहौल में एक जैसा बने रहने की सुविधा मिलती है. साथ ही, यह प्रोजेक्ट की डिपेंडेंसी में हुए बदलावों से, रिज़ॉल्यूशन प्रोसेस के उन हिस्सों को स्किप करने की अनुमति देकर, बिल्ड की परफ़ॉर्मेंस को बेहतर बनाता है जिन पर इन बदलावों का कोई असर नहीं पड़ता. इसके अलावा, लॉकफ़ाइल, बाहरी लाइब्रेरी में अचानक होने वाले अपडेट या बदलावों को रोककर, ऐप्लिकेशन को क्रैश या फ़्रीज़ होने से बचाती है. इससे, बग होने का जोखिम भी कम हो जाता है.
लॉकफ़ाइल जनरेट करना
लॉकफ़ाइल, वर्कस्पेस के रूट में MODULE.bazel.lock
नाम से जनरेट होती है. इसे बिल्ड करने की प्रोसेस के दौरान बनाया या अपडेट किया जाता है. खास तौर पर, मॉड्यूल रिज़ॉल्यूशन और एक्सटेंशन के आकलन के बाद. अहम बात यह है कि इसमें सिर्फ़ वे डिपेंडेंसी शामिल होती हैं जो बिल्ड के मौजूदा अनुरोध में शामिल होती हैं.
जब प्रोजेक्ट में ऐसे बदलाव होते हैं जिनका असर उसकी डिपेंडेंसी पर पड़ता है, तो नई स्थिति दिखाने के लिए लॉकफ़ाइल अपने-आप अपडेट हो जाती है. इससे यह पक्का होता है कि लॉकफ़ाइल, मौजूदा बिल्ड के लिए ज़रूरी डिपेंडेंसी के खास सेट पर फ़ोकस करती रहे. साथ ही, प्रोजेक्ट की हल की गई डिपेंडेंसी के बारे में सटीक जानकारी देती रहे.
लॉकफ़ाइल का इस्तेमाल
जब प्रोजेक्ट की स्थिति, लॉकफ़ाइल से अलग हो, तो Bazel के व्यवहार को पसंद के मुताबिक बनाने के लिए, लॉकफ़ाइल को फ़्लैग --lockfile_mode
से कंट्रोल किया जा सकता है. ये मोड उपलब्ध हैं:
update
(डिफ़ॉल्ट): लॉकफ़ाइल में मौजूद जानकारी का इस्तेमाल करके, पहले से मौजूद रजिस्ट्री फ़ाइलों को डाउनलोड करने से बचें. साथ ही, उन एक्सटेंशन का फिर से आकलन करने से बचें जिनके नतीजे अब भी अप-टू-डेट हैं. अगर कोई जानकारी मौजूद नहीं है, तो उसे लॉकफ़ाइल में जोड़ दिया जाएगा. इस मोड में, Bazel उन डिपेंडेंसी के लिए बदली जा सकने वाली जानकारी को रीफ़्रेश करने से भी बचता है जिनमें कोई बदलाव नहीं हुआ है. जैसे, हटाए गए वर्शन.refresh
:update
की तरह ही, लेकिन इस मोड पर स्विच करने पर और इस मोड में हर घंटे, बदलाव की जा सकने वाली जानकारी हमेशा रीफ़्रेश होती रहती है.error
:update
की तरह ही, लेकिन अगर कोई जानकारी मौजूद नहीं है या वह पुरानी है, तो Bazel गड़बड़ी के साथ काम नहीं करेगा. यह मोड, रिज़ॉल्यूशन के दौरान लॉकफ़ाइल में कभी बदलाव नहीं करता या नेटवर्क अनुरोध नहीं करता.reproducible
के तौर पर मार्क किए गए मॉड्यूल एक्सटेंशन, अब भी नेटवर्क अनुरोध कर सकते हैं. हालांकि, उनसे हमेशा एक ही नतीजा मिलने की उम्मीद की जाती है.off
: लॉकफ़ाइल की जांच नहीं की जाती और न ही उसे अपडेट किया जाता है.
लॉकफ़ाइल के फ़ायदे
लॉकफ़ाइल के कई फ़ायदे हैं और इसका इस्तेमाल कई तरीकों से किया जा सकता है:
फिर से बनाए जा सकने वाले बिल्ड. सॉफ़्टवेयर लाइब्रेरी के खास वर्शन या डिपेंडेंसी कैप्चर करके, लॉकफ़ाइल यह पक्का करती है कि अलग-अलग एनवायरमेंट में और समय के साथ, बिल्ड फिर से बनाए जा सकें. डेवलपर अपने प्रोजेक्ट बनाते समय, एक जैसे और अनुमानित नतीजों पर भरोसा कर सकते हैं.
तेज़ी से बढ़ने वाले रिज़ॉल्यूशन. लॉकफ़ाइल की मदद से, Bazel उन रजिस्ट्री फ़ाइलों को डाउनलोड करने से बचता है जिन्हें पहले के किसी बिल्ड में पहले ही इस्तेमाल किया जा चुका है. इससे बिल्ड की परफ़ॉर्मेंस काफ़ी बेहतर होती है. खास तौर पर, उन मामलों में जहां समस्या हल करने में ज़्यादा समय लग सकता है.
स्टैबिलिटी और जोखिम को कम करना. लॉकफ़ाइल, अचानक होने वाले अपडेट या बाहरी लाइब्रेरी में बदलावों को रोककर, ऐप्लिकेशन को स्थिर रखने में मदद करती है. डिपेंडेंसी को किसी खास वर्शन पर लॉक करने से, काम न करने वाले या जिनकी जांच नहीं की गई है ऐसे अपडेट की वजह से गड़बड़ियों का खतरा कम हो जाता है.
लॉकफ़ाइल का कॉन्टेंट
लॉकफ़ाइल में वह सभी ज़रूरी जानकारी होती है जिससे यह पता चलता है कि प्रोजेक्ट की स्थिति में बदलाव हुआ है या नहीं. इसमें मौजूदा स्थिति में प्रोजेक्ट को बिल्ड करने का नतीजा भी शामिल होता है. लॉकफ़ाइल में दो मुख्य हिस्से होते हैं:
- मॉड्यूल रिज़ॉल्यूशन के इनपुट के तौर पर इस्तेमाल होने वाली सभी रिमोट फ़ाइलों के हैश.
- हर मॉड्यूल एक्सटेंशन के लिए, लॉकफ़ाइल में ऐसे इनपुट शामिल होते हैं जिनका उस पर असर पड़ता है. इनपुट को
bzlTransitiveDigest
,usagesDigest
, और अन्य फ़ील्ड से दिखाया जाता है. साथ ही, उस एक्सटेंशन को चलाने का आउटपुट भी शामिल होता है, जिसेgeneratedRepoSpecs
कहा जाता है
यहां एक उदाहरण दिया गया है, जिसमें लॉकफ़ाइल के स्ट्रक्चर के साथ-साथ, हर सेक्शन के बारे में जानकारी दी गई है:
{
"lockFileVersion": 10,
"registryFileHashes": {
"https://bcr.bazel.build/bazel_registry.json": "8a28e4af...5d5b3497",
"https://bcr.bazel.build/modules/foo/1.0/MODULE.bazel": "7cd0312e...5c96ace2",
"https://bcr.bazel.build/modules/foo/2.0/MODULE.bazel": "70390338... 9fc57589",
"https://bcr.bazel.build/modules/foo/2.0/source.json": "7e3a9adf...170d94ad",
"https://registry.mycorp.com/modules/foo/1.0/MODULE.bazel": "not found",
...
},
"selectedYankedVersions": {
"foo@2.0": "Yanked for demo purposes"
},
"moduleExtensions": {
"//:extension.bzl%lockfile_ext": {
"general": {
"bzlTransitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
"usagesDigest": "aLmqbvowmHkkBPve05yyDNGN7oh7QE9kBADr3QIZTZs=",
...,
"generatedRepoSpecs": {
"hello": {
"bzlFile": "@@//:extension.bzl",
...
}
}
}
},
"//:extension.bzl%lockfile_ext2": {
"os:macos": {
"bzlTransitiveDigest": "oWDzxG/aLnyY6Ubrfy....+Jp6maQvEPxn0pBM=",
"usagesDigest": "aLmqbvowmHkkBPve05y....yDNGN7oh7r3QIZTZs=",
...,
"generatedRepoSpecs": {
"hello": {
"bzlFile": "@@//:extension.bzl",
...
}
}
},
"os:linux": {
"bzlTransitiveDigest": "eWDzxG/aLsyY3Ubrto....+Jp4maQvEPxn0pLK=",
"usagesDigest": "aLmqbvowmHkkBPve05y....yDNGN7oh7r3QIZTZs=",
...,
"generatedRepoSpecs": {
"hello": {
"bzlFile": "@@//:extension.bzl",
...
}
}
}
}
}
}
रजिस्ट्री फ़ाइल के हैश
registryFileHashes
सेक्शन में, मॉड्यूल रिज़ॉल्यूशन के दौरान ऐक्सेस की गई रिमोट रजिस्ट्री की सभी फ़ाइलों के हैश शामिल होते हैं. एक ही इनपुट देने पर, रिज़ॉल्यूशन एल्गोरिदम पूरी तरह से तय होता है और सभी रिमोट इनपुट को हैश किया जाता है. इससे, लॉकफ़ाइल में रिमोट जानकारी के डुप्लीकेट होने से बचा जा सकता है. साथ ही, यह भी पक्का होता है कि रिज़ॉल्यूशन का नतीजा पूरी तरह से दोहराया जा सकता है. ध्यान दें कि अगर किसी खास रजिस्ट्री में कोई मॉड्यूल मौजूद नहीं है, लेकिन प्राथमिकता में कम रजिस्ट्री में वह मॉड्यूल मौजूद है, तो भी रिकॉर्डिंग की ज़रूरत होती है. उदाहरण में, "नहीं मिला" एंट्री देखें. इस जानकारी को bazel mod deps --lockfile_mode=refresh
की मदद से अपडेट किया जा सकता है.
Bazel, लॉकफ़ाइल के हैश का इस्तेमाल करके, रिपॉज़िटरी कैश में मौजूद रजिस्ट्री फ़ाइलों को डाउनलोड करने से पहले उन्हें खोजता है. इससे, बाद में होने वाले रिज़ॉल्यूशन की प्रोसेस तेज़ हो जाती है.
हटाए गए चुनिंदा वर्शन
selectedYankedVersions
सेक्शन में, मॉड्यूल के उन वर्शन होते हैं जिन्हें मॉड्यूल रिज़ॉल्यूशन के हिसाब से चुना गया था. आम तौर पर, बाइनरी बनाने की कोशिश करने पर गड़बड़ी का मैसेज दिखता है. इसलिए, यह सेक्शन सिर्फ़ तब नहीं खाली होता, जब --allow_yanked_versions
या BZLMOD_ALLOW_YANKED_VERSIONS
के ज़रिए, हटाए गए वर्शन को साफ़ तौर पर अनुमति दी गई हो.
इस फ़ील्ड की ज़रूरत इसलिए है, क्योंकि मॉड्यूल फ़ाइलों की तुलना में, हटाए गए वर्शन की जानकारी में बदलाव किया जा सकता है. इसलिए, इसका रेफ़रंस हैश से नहीं दिया जा सकता. इस जानकारी को bazel mod deps --lockfile_mode=refresh
की मदद से अपडेट किया जा सकता है.
मॉड्यूल एक्सटेंशन
moduleExtensions
सेक्शन एक ऐसा मैप है जिसमें सिर्फ़ वे एक्सटेंशन शामिल होते हैं जिनका इस्तेमाल, मौजूदा या पहले किए गए अनुरोध में किया गया है. इसमें ऐसे एक्सटेंशन शामिल नहीं होते जिनका अब इस्तेमाल नहीं किया जा रहा है. दूसरे शब्दों में, अगर किसी एक्सटेंशन का इस्तेमाल अब डिपेंडेंसी ग्राफ़ में नहीं किया जा रहा है, तो उसे moduleExtensions
मैप से हटा दिया जाता है.
अगर कोई एक्सटेंशन, ऑपरेटिंग सिस्टम या आर्किटेक्चर टाइप पर निर्भर नहीं है, तो इस सेक्शन में सिर्फ़ एक "सामान्य" एंट्री होती है. ऐसा न होने पर, कई एंट्री शामिल की जाती हैं. इन एंट्री को ओएस, आर्किटेक्चर या दोनों के नाम पर रखा जाता है. हर एंट्री, उन खास बातों के आधार पर एक्सटेंशन का आकलन करने के नतीजे से जुड़ी होती है.
एक्सटेंशन मैप में मौजूद हर एंट्री, इस्तेमाल किए गए एक्सटेंशन से जुड़ी होती है. साथ ही, इसकी पहचान, उसमें मौजूद फ़ाइल और नाम से की जाती है. हर एंट्री के लिए, उससे जुड़ी वैल्यू में उस एक्सटेंशन से जुड़ी ज़रूरी जानकारी होती है:
bzlTransitiveDigest
, एक्सटेंशन लागू करने के डाइजेस्ट और उससे ट्रांज़िशन के तौर पर लोड की गई .bzl फ़ाइलों का डाइजेस्ट होता है.usagesDigest
, डिपेंडेंसी ग्राफ़ में एक्सटेंशन के इस्तेमाल का डाइजेस्ट होता है. इसमें सभी टैग शामिल होते हैं.- ऐसे अन्य फ़ील्ड जिनके बारे में नहीं बताया गया है. ये एक्सटेंशन के अन्य इनपुट को ट्रैक करते हैं. जैसे, पढ़ी जाने वाली फ़ाइलों या डायरेक्ट्री का कॉन्टेंट या इस्तेमाल किए जाने वाले एनवायरमेंट वैरिएबल.
generatedRepoSpecs
, मौजूदा इनपुट की मदद से एक्सटेंशन से बनाई गई रिपॉज़िटरी को एन्कोड करता है.- ज़रूरी नहीं
moduleExtensionMetadata
फ़ील्ड में, एक्सटेंशन से मिला मेटाडेटा होता है. जैसे, क्या इसके बनाए गए कुछ डेटा स्टोर को रूट मॉड्यूल केuse_repo
से इंपोर्ट किया जाना चाहिए. इस जानकारी की मदद से,bazel mod tidy
कमांड काम करता है.
मॉड्यूल एक्सटेंशन, reproducible = True
के साथ रिटर्निंग मेटाडेटा सेट करके, लॉकफ़ाइल में शामिल होने से ऑप्ट आउट कर सकते हैं. ऐसा करने से, वे यह वादा करते हैं कि एक जैसे इनपुट मिलने पर, वे हमेशा एक जैसे डेटा स्टोर बनाए रखेंगे.
सबसे सही तरीके
लॉकफ़ाइल की सुविधा का ज़्यादा से ज़्यादा फ़ायदा पाने के लिए, ये सबसे सही तरीके अपनाएं:
प्रोजेक्ट की डिपेंडेंसी या कॉन्फ़िगरेशन में हुए बदलावों को दिखाने के लिए, लॉकफ़ाइल को नियमित तौर पर अपडेट करें. इससे यह पक्का होता है कि बाद के बिल्ड, डिपेंडेंसी के सबसे अप-टू-डेट और सटीक सेट पर आधारित हों. सभी एक्सटेंशन को एक साथ लॉक करने के लिए,
bazel mod deps --lockfile_mode=update
चलाएं.साथ मिलकर काम करने की सुविधा देने के लिए, वर्शन कंट्रोल में लॉकफ़ाइल शामिल करें. साथ ही, पक्का करें कि टीम के सभी सदस्यों के पास एक ही लॉकफ़ाइल का ऐक्सेस हो. इससे प्रोजेक्ट में एक जैसा डेवलपमेंट एनवायरमेंट बनता है.
Bazel को चलाने के लिए,
bazelisk
का इस्तेमाल करें. साथ ही, वर्शन कंट्रोल में.bazelversion
फ़ाइल शामिल करें. इससे, लॉकफ़ाइल से जुड़े Bazel वर्शन के बारे में पता चलता है. Bazel आपके बिल्ड की डिपेंडेंसी है. इसलिए, लॉकफ़ाइल, Bazel के वर्शन के हिसाब से बनाई जाती है. यह पिछले वर्शन के साथ काम करने वाले Bazel रिलीज़ के बीच भी बदल जाएगी.bazelisk
का इस्तेमाल करने से यह पक्का होता है कि सभी डेवलपर, लॉकफ़ाइल से मैच करने वाले Bazel वर्शन का इस्तेमाल कर रहे हैं.
इन सबसे सही तरीकों का पालन करके, Bazel में लॉकफ़ाइल की सुविधा का बेहतर तरीके से इस्तेमाल किया जा सकता है. इससे, सॉफ़्टवेयर डेवलपमेंट वर्कफ़्लो ज़्यादा असरदार, भरोसेमंद, और साथ मिलकर काम करने वाले बनते हैं.
डेटा में अंतर को मर्ज करना
लॉकफ़ाइल फ़ॉर्मैट को मर्ज करने से जुड़ी समस्याओं को कम करने के लिए डिज़ाइन किया गया है. हालांकि, ये समस्याएं अब भी हो सकती हैं.
अपने-आप रिज़ॉल्यूशन तय होना
Bazel, इन विरोधों को अपने-आप हल करने में मदद करने के लिए, एक कस्टम git मर्ज ड्राइवर उपलब्ध कराता है.
ड्राइवर को सेट अप करने के लिए, अपनी git रिपॉज़िटरी के रूट में मौजूद .gitattributes
फ़ाइल में यह लाइन जोड़ें:
# A custom merge driver for the Bazel lockfile.
# https://bazel.build/external/lockfile#automatic-resolution
MODULE.bazel.lock merge=bazel-lockfile-merge
इसके बाद, ड्राइवर का इस्तेमाल करने वाले हर डेवलपर को इसे एक बार रजिस्टर करना होगा. इसके लिए, यह तरीका अपनाएं:
- jq (1.5 या इसके बाद का वर्शन) इंस्टॉल करें.
- ये कमांड चलाएं:
jq_script=$(curl https://raw.githubusercontent.com/bazelbuild/bazel/master/scripts/bazel-lockfile-merge.jq)
printf '%s\n' "${jq_script}" | less # to optionally inspect the jq script
git config --global merge.bazel-lockfile-merge.name "Merge driver for the Bazel lockfile (MODULE.bazel.lock)"
git config --global merge.bazel-lockfile-merge.driver "jq -s '${jq_script}' -- %O %A %B > %A.jq_tmp && mv %A.jq_tmp %A"
मैन्युअल तरीके से रिज़ॉल्यूशन तय करना
registryFileHashes
और selectedYankedVersions
फ़ील्ड में, मर्ज करने से जुड़ी समस्याओं को आसानी से हल किया जा सकता है. इसके लिए, दोनों तरफ़ की सभी एंट्री को रखें.
मर्ज करने से जुड़े अन्य तरह के विवादों को मैन्युअल तरीके से हल नहीं किया जाना चाहिए. इसके बजाय:
git reset MODULE.bazel.lock && git checkout MODULE.bazel.lock
का इस्तेमाल करके, लॉकफ़ाइल की पिछली स्थिति को वापस लाएं.MODULE.bazel
फ़ाइल में मौजूद किसी भी तरह के विरोध को हल करें.- लॉकफ़ाइल अपडेट करने के लिए,
bazel mod deps
चलाएं.