Bazel लॉकफ़ाइल

Bazel में लॉकफ़ाइल की सुविधा की मदद से, किसी प्रोजेक्ट के लिए ज़रूरी सॉफ़्टवेयर लाइब्रेरी या पैकेज के खास वर्शन या डिपेंडेंसी रिकॉर्ड की जा सकती हैं. यह सुविधा, मॉड्यूल रिज़ॉल्यूशन और एक्सटेंशन के आकलन के नतीजों को सेव करके, ऐसा करती है. लॉकफ़ाइल की मदद से, एक जैसे बिल्ड तैयार किए जा सकते हैं. इससे, डेवलपमेंट के एनवायरमेंट में एकरूपता बनी रहती है. इसके अलावा, लॉकफ़ाइल की मदद से बिल्ड की क्षमता बढ़ती है. ऐसा इसलिए, क्योंकि Bazel, रिज़ॉल्यूशन की प्रोसेस के उन हिस्सों को छोड़ सकता है जिन पर प्रोजेक्ट की डिपेंडेंसी में हुए बदलावों का असर नहीं पड़ता. साथ ही, लॉकफ़ाइल की मदद से स्थिरता बेहतर होती है. ऐसा इसलिए, क्योंकि यह बाहरी लाइब्रेरी में अनचाहे अपडेट या बड़े बदलावों को रोकती है. इससे, गड़बड़ियां होने का खतरा कम हो जाता है.

लॉकफ़ाइल जनरेट करना

लॉकफ़ाइल, वर्कस्पेस के रूट में MODULE.bazel.lock नाम से जनरेट होती है. यह बिल्ड की प्रोसेस के दौरान बनाई या अपडेट की जाती है. खास तौर पर, मॉड्यूल रिज़ॉल्यूशन और एक्सटेंशन के आकलन के बाद. अहम बात यह है कि इसमें सिर्फ़ वे डिपेंडेंसी शामिल होती हैं जो बिल्ड के मौजूदा इनवोकेशन में शामिल हैं.

जब प्रोजेक्ट में ऐसे बदलाव होते हैं जिनसे उसकी डिपेंडेंसी पर असर पड़ता है, तो लॉकफ़ाइल अपने-आप अपडेट हो जाती है, ताकि नई स्थिति दिख सके. इससे यह पक्का होता है कि लॉकफ़ाइल, मौजूदा बिल्ड के लिए ज़रूरी डिपेंडेंसी के खास सेट पर फ़ोकस रहे. साथ ही, प्रोजेक्ट की हल की गई डिपेंडेंसी की सटीक जानकारी मिले.

लॉकफ़ाइल का इस्तेमाल करना

लॉकफ़ाइल को फ़्लैग --lockfile_mode से कंट्रोल किया जा सकता है. इससे, प्रोजेक्ट की स्थिति, लॉकफ़ाइल से अलग होने पर, Bazel के व्यवहार को पसंद के मुताबिक बनाया जा सकता है. ये मोड उपलब्ध हैं:

  • update (डिफ़ॉल्ट): लॉकफ़ाइल में मौजूद जानकारी का इस्तेमाल करके, जानी-मानी रजिस्ट्री फ़ाइलों के डाउनलोड को छोड़ें. साथ ही, उन एक्सटेंशन का फिर से आकलन न करें जिनके नतीजे अब भी अप-टू-डेट हैं. अगर जानकारी मौजूद नहीं है, तो उसे लॉकफ़ाइल में जोड़ा जाएगा. इस मोड में, Bazel उन डिपेंडेंसी के लिए, बदली जा सकने वाली जानकारी को रीफ़्रेश नहीं करता जिनमें कोई बदलाव नहीं हुआ है. जैसे, हटाए गए वर्शन.
  • refresh: यह मोड update की तरह काम करता है. हालांकि, इस मोड पर स्विच करने पर, बदली जा सकने वाली जानकारी हमेशा रीफ़्रेश होती है. साथ ही, इस मोड में हर घंटे में एक बार रीफ़्रेश होती है.
  • error: यह मोड update की तरह काम करता है. हालांकि, अगर कोई जानकारी मौजूद नहीं है या पुरानी है, तो Bazel गड़बड़ी के साथ फ़ेल हो जाएगा. यह मोड, रिज़ॉल्यूशन के दौरान लॉकफ़ाइल में कभी भी बदलाव नहीं करता या नेटवर्क के अनुरोध नहीं भेजता. मॉड्यूल एक्सटेंशन, जिन्हें reproducible के तौर पर मार्क किया गया है वे अब भी नेटवर्क के अनुरोध भेज सकते हैं. हालांकि, उनसे हमेशा एक जैसा नतीजा मिलने की उम्मीद की जाती है.
  • off: इस मोड में, लॉकफ़ाइल की जांच नहीं की जाती और न ही उसे अपडेट किया जाता है.

लॉकफ़ाइल के फ़ायदे

लॉकफ़ाइल के कई फ़ायदे हैं और इसका इस्तेमाल कई तरीकों से किया जा सकता है:

  • एक जैसे बिल्ड तैयार किए जा सकते हैं. सॉफ़्टवेयर लाइब्रेरी के खास वर्शन या डिपेंडेंसी कैप्चर करके, लॉकफ़ाइल यह पक्का करती है कि अलग-अलग एनवायरमेंट में और समय के साथ, एक जैसे बिल्ड तैयार किए जा सकें. डेवलपर, अपने प्रोजेक्ट बनाते समय, एक जैसे और अनुमान के मुताबिक नतीजे पा सकते हैं.

  • इंक्रीमेंटल रिज़ॉल्यूशन तेज़ी से किया जा सकता है. लॉकफ़ाइल की मदद से, Bazel उन रजिस्ट्री फ़ाइलों को डाउनलोड नहीं करता जिनका इस्तेमाल पहले किसी बिल्ड में किया गया था. इससे, बिल्ड की क्षमता काफ़ी बेहतर होती है. खास तौर पर, उन स्थितियों में जहां रिज़ॉल्यूशन में ज़्यादा समय लग सकता है.

  • स्थिरता बनी रहती है और जोखिम कम होता है. लॉकफ़ाइल की मदद से, स्थिरता बनी रहती है. ऐसा इसलिए, क्योंकि यह बाहरी लाइब्रेरी में अनचाहे अपडेट या बड़े बदलावों को रोकती है. डिपेंडेंसी को खास वर्शन पर लॉक करके, असंगत या बिना जांच किए गए अपडेट की वजह से गड़बड़ियां होने का खतरा कम हो जाता है.

लॉकफ़ाइल का कॉन्टेंट

लॉकफ़ाइल में, यह तय करने के लिए सभी ज़रूरी जानकारी होती है कि प्रोजेक्ट की स्थिति में बदलाव हुआ है या नहीं. इसमें, मौजूदा स्थिति में प्रोजेक्ट बनाने का नतीजा भी शामिल होता है. लॉकफ़ाइल के दो मुख्य हिस्से होते हैं:

  1. मॉड्यूल रिज़ॉल्यूशन के लिए इनपुट के तौर पर इस्तेमाल की जाने वाली सभी रिमोट फ़ाइलों के हैश.
  2. हर मॉड्यूल एक्सटेंशन के लिए, लॉकफ़ाइल में वे इनपुट शामिल होते हैं जिन पर इसका असर पड़ता है, इन्हें 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 मैप से हटा दिया जाता है.

अगर कोई एक्सटेंशन, ऑपरेटिंग सिस्टम या आर्किटेक्चर टाइप से अलग है, तो इस सेक्शन में सिर्फ़ एक "सामान्य" एंट्री दिखती है. इसके अलावा, इसमें कई एंट्री शामिल होती हैं. इनके नाम, ओएस, आर्किटेक्चर या दोनों के हिसाब से रखे जाते हैं. हर एंट्री, उन खास चीज़ों पर एक्सटेंशन का आकलन करने के नतीजे से जुड़ी होती है.

एक्सटेंशन मैप में मौजूद हर एंट्री, इस्तेमाल किए गए एक्सटेंशन से जुड़ी होती है. इसकी पहचान, इसमें शामिल फ़ाइल और नाम से होती है. हर एंट्री की वैल्यू में, उस एक्सटेंशन से जुड़ी काम की जानकारी शामिल होती है:

  1. bzlTransitiveDigest, एक्सटेंशन के लागू करने और उसके ज़रिए ट्रांज़िटिव तरीके से लोड की गई .bzl फ़ाइलों का डाइजेस्ट होता है.
  2. usagesDigest, डिपेंडेंसी ग्राफ़ में एक्सटेंशन के इस्तेमाल का डाइजेस्ट होता है. इसमें सभी टैग शामिल होते हैं.
  3. इसके अलावा, ऐसे फ़ील्ड जिनकी जानकारी नहीं दी गई है. ये फ़ील्ड, एक्सटेंशन के अन्य इनपुट को ट्रैक करते हैं. जैसे, उन फ़ाइलों या डायरेक्ट्री का कॉन्टेंट जिन्हें वह पढ़ता है या उन एनवायरमेंट वैरिएबल का कॉन्टेंट जिनका वह इस्तेमाल करता है.
  4. generatedRepoSpecs, मौजूदा इनपुट के साथ एक्सटेंशन से बनाई गई रिपॉज़िटरी को एनकोड करता है.
  5. ज़रूरी नहीं है कि moduleExtensionMetadata फ़ील्ड में, एक्सटेंशन से दिया गया मेटाडेटा शामिल हो. जैसे, एक्सटेंशन से बनाई गई कुछ रिपॉज़िटरी को रूट मॉड्यूल से use_repo के ज़रिए इंपोर्ट किया जाना चाहिए या नहीं. इस जानकारी से, bazel mod tidy कमांड काम करता है.

मॉड्यूल एक्सटेंशन, लॉकफ़ाइल में शामिल न होने का विकल्प चुन सकते हैं. इसके लिए, reproducible = True के साथ, वापस आने वाले मेटाडेटा को सेट करें. ऐसा करने पर, वे वादा करते हैं कि एक जैसे इनपुट दिए जाने पर, वे हमेशा एक जैसी रिपॉज़िटरी बनाएंगे.

सबसे सही तरीके

लॉकफ़ाइल की सुविधा के फ़ायदों को बढ़ाने के लिए, यहां दिए गए सबसे सही तरीकों को अपनाएं:

  • प्रोजेक्ट की डिपेंडेंसी या कॉन्फ़िगरेशन में हुए बदलावों को दिखाने के लिए, लॉकफ़ाइल को समय-समय पर अपडेट करें. इससे यह पक्का होता है कि बाद के बिल्ड, डिपेंडेंसी के सबसे अप-टू-डेट और सटीक सेट पर आधारित हों. सभी एक्सटेंशन को एक साथ लॉक करने के लिए, bazel mod deps --lockfile_mode=update चलाएं.

  • वर्शन कंट्रोल में लॉकफ़ाइल शामिल करें, ताकि साथ मिलकर काम किया जा सके. साथ ही, यह पक्का करें कि टीम के सभी सदस्यों के पास एक ही लॉकफ़ाइल का ऐक्सेस हो. इससे, प्रोजेक्ट में डेवलपमेंट के एनवायरमेंट में एकरूपता बनी रहती है.

  • Bazel चलाने के लिए, bazelisk का इस्तेमाल करें. साथ ही, वर्शन कंट्रोल में a .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

इसके बाद, ड्राइवर का इस्तेमाल करने वाले हर डेवलपर को, इन चरणों को फ़ॉलो करके, इसे एक बार रजिस्टर करना होगा:

  1. jq (1.5 या इससे नया वर्शन) इंस्टॉल करें.
  2. ये कमांड चलाएं:
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 फ़ील्ड में, मर्ज करने से जुड़ी सामान्य समस्याओं को सुरक्षित तरीके से हल किया जा सकता है. इसके लिए, समस्या के दोनों पक्षों की सभी एंट्री को सेव रखें.

मर्ज करने से जुड़ी अन्य समस्याओं को मैन्युअल तरीके से हल नहीं किया जाना चाहिए. इसके बजाय:

  1. लॉकफ़ाइल की पिछली स्थिति को वापस लाएं git reset MODULE.bazel.lock && git checkout MODULE.bazel.lock की मदद से.
  2. MODULE.bazel फ़ाइल में मौजूद किसी भी समस्या को हल करें.
  3. लॉकफ़ाइल को अपडेट करने के लिए, bazel mod deps चलाएं.