Bazel लॉकफ़ाइल

7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

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

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

Lockfile का इस्तेमाल

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

  • 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 सेक्शन में, मॉड्यूल रिज़ॉल्यूशन के दौरान ऐक्सेस की गई रिमोट रजिस्ट्री की सभी फ़ाइलों के हैश शामिल होते हैं. एक जैसे इनपुट और सभी रिमोट इनपुट हैश किए जाने पर, रिज़ॉल्यूशन एल्गोरिदम पूरी तरह से तय होता है. इससे, पूरी तरह से दोबारा जनरेट हो सकने वाले रिज़ॉल्यूशन के नतीजे को पक्का किया जा सकता है. साथ ही, Lockfile में रिमोट जानकारी को बार-बार दिखाने से बचा जा सकता है. ध्यान दें कि अगर किसी खास रजिस्ट्री में कोई मॉड्यूल मौजूद नहीं है, लेकिन प्राथमिकता में कम रजिस्ट्री में वह मॉड्यूल मौजूद है, तो भी रिकॉर्डिंग की ज़रूरत होती है. उदाहरण में, "नहीं मिला" एंट्री देखें. इस जानकारी को bazel mod deps --lockfile_mode=refresh की मदद से अपडेट किया जा सकता है.

Basel, रजिस्ट्री फ़ाइलों को डाउनलोड करने से पहले और उन्हें रिपॉज़िटरी की कैश मेमोरी में सेव करने के लिए, lockfile से हैश का इस्तेमाल करता है. इससे, बाद के रिज़ॉल्यूशन तेज़ी से बढ़ जाते हैं.

यान वाले वर्शन के चुने गए वर्शन

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 की मदद से सेट करके, lockfile में शामिल किए जाने से ऑप्ट आउट कर सकते हैं. ऐसा करके, वे वादा करते हैं कि एक जैसे इनपुट दिए जाने पर वे हमेशा एक ही डेटा स्टोर करने की जगह बनाएंगे.

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

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

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

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

  • Bazel को चलाने के लिए, bazelisk का इस्तेमाल करें. साथ ही, वर्शन कंट्रोल में .bazelversion फ़ाइल शामिल करें. इससे, लॉकफ़ाइल से जुड़े Bazel वर्शन के बारे में पता चलता है. Baज़ल, आपके बिल्ड पर निर्भर करता है. इसलिए, Lockfile खास तौर पर Basel के वर्शन के हिसाब से होता है. साथ ही, वह पुराने सिस्टम के साथ काम करने वाले बैज की रिलीज़ के बीच में भी बदल सकता है. bazelisk का इस्तेमाल करने से यह पक्का होता है कि सभी डेवलपर, लॉकफ़ाइल से मैच करने वाले Bazel वर्शन का इस्तेमाल कर रहे हैं.

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

विरोधों को मिलाएं

लॉकफ़ाइल फ़ॉर्मैट को मर्ज करने से जुड़ी समस्याओं को कम करने के लिए डिज़ाइन किया गया है. हालांकि, ये समस्याएं अब भी हो सकती हैं.

स्वचालित रिज़ॉल्यूशन

इन विवादों को अपने-आप हल करने में मदद के लिए, Bagel एक कस्टम 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 चलाएं.