कंटीन्यूअस इंटीग्रेशन सेटअप कीजिए


अपने ट्रैक के लिए कंटीन्यूअस इंटीग्रेशन (CI) सेट अप करना बहुत ज़रूरी है, क्योंकि इससे गलतियाँ पकड़ने में मदद मिलती है।

GitHub Actions

Exercism के रेपो (ट्रैक रेपो सहित) अपनी CI चलाने के लिए GitHub Actions का उपयोग करते हैं। GitHub Actions वर्कफ्लो पर आधारित होते हैं, जो तय करते हैं कि कोई खास घटना होने पर (जैसे कोई कमिट पुश करना) कौन-सी स्क्रिप्ट अपने आप चलनी है। GitHub Actions वर्कफ्लो के बारे में अधिक जानकारी के लिए वर्कफ्लो डॉक्स देखिए।

पहले से इंस्टॉल वर्कफ्लो

ट्रैक पहले से ही कई वर्कफ्लो के साथ आते हैं, जिनमें से ज़्यादातर को आपको नहीं बदलना चाहिए (इन्हें शेयर्ड वर्कफ्लो कहा जाता है)। हाँ, एक वर्कफ्लो ऐसा है जिसे आपको बदलना चाहिए, और वह है test.yml वर्कफ्लो।

टेस्ट वर्कफ्लो

test.yml वर्कफ्लो का उद्देश्य यह जाँचना है कि ट्रैक के अभ्यास ठीक हालत में हैं। यह वर्कफ्लो अपने आप चलने के लिए सेट किया गया है (GitHub Actions की शब्दावली में: ट्रिगर होता है) जब main ब्रांच या किसी पुल रिक्वेस्ट की ब्रांच पर पुश किया जाता है।

वर्कफ्लो को खुद ज़्यादा कुछ करना नहीं है, इनके अलावा:

  • कोड चेकआउट करना (पहले से लागू)
  • डिपेंडेंसी इंस्टॉल करना (जैसे पैकेज इंस्टॉल करना, वैकल्पिक)
  • टूल इंस्टॉल करना (जैसे कोई SDK इंस्टॉल करना, वैकल्पिक)
  • अभ्यास जाँचने वाली स्क्रिप्ट चलाना (पहले से लागू)

अभ्यास जाँचने वाली स्क्रिप्ट लागू कीजिए

जैसा कि बताया गया, अभ्यासों की जाँच एक स्क्रिप्ट से होती है, यानी bin/verify-exercises (bash) स्क्रिप्ट से। यह स्क्रिप्ट लगभग तैयार है, और यह निम्न काम करती है:

  • सभी अभ्यास डायरेक्टरियों पर लूप चलाती है
  • फिर हर अभ्यास डायरेक्टरी के लिए यह:
    • उदाहरण/एक्ज़ेंप्लर हल को (स्टब) हल फाइलों में कॉपी करती है (पहले से लागू)
    • unskip_tests फंक्शन को कॉल करती है, जिसमें आप अपनी टेस्ट फाइलों के टेस्ट अनस्किप कर सकते हैं (वैकल्पिक)
    • run_tests फंक्शन को कॉल करती है, जिसमें आपको टेस्ट चलाने हैं (आवश्यक)

सिर्फ run_tests और unskip_tests फंक्शन ही वे चीज़ें हैं जिन्हें आपको लागू करना है।

टेस्ट अनस्किप कीजिए

अगर आपका ट्रैक टेस्ट स्किप करने का समर्थन करता है, तो हमें यह सुनिश्चित करना होगा कि किसी अभ्यास के उदाहरण/एक्ज़ेंप्लर हल की जाँच करते समय कोई टेस्ट स्किप न हो। आम तौर पर ट्रैक टेस्ट "अनस्किप" करने के दो तरीके अपनाते हैं:

  1. टेस्ट फाइलों से एनोटेशन/कोड/टेक्स्ट हटाना। जैसे test.skip को test में बदलना।
  2. कोई एनवायरनमेंट वेरिएबल देना। जैसे SKIP_TESTS=false सेट करना।

टेस्ट फाइलों से एनोटेशन/कोड/टेक्स्ट हटाना

अगर टेस्ट स्किप करना फाइल-आधारित है (ऊपर बताया गया पहला विकल्प), तो टेस्ट फाइलों को बदलने के लिए unskip_tests फंक्शन में बदलाव कीजिए (मौजूदा कोड टेस्ट फाइलों पर लूप चलाने का काम पहले से करता है)।

Note

unskip_test फंक्शन अभ्यास डायरेक्टरी की एक कॉपी पर चलता है, इसलिए फाइलों में जैसा आपको उचित लगे वैसा बदलाव बेझिझक कीजिए।

उदाहरण

Arturo ट्रैक की bin/verify-exercises file टेस्ट फाइलों के भीतर टेस्ट अनस्किप करने के लिए sed का उपयोग करती है:

unskip_tests() {
    jq -r '.files.test[]' .meta/config.json | while read -r test_file; do
        sed -i 's/test.skip/test/g' "${test_file}"
    done
}

एनवायरनमेंट वेरिएबल देना

Caution

अगर टेस्ट अनस्किप करने के लिए कोई एनवायरनमेंट वेरिएबल सेट करना ज़रूरी है, तो पक्का कीजिए कि वह run_tests फंक्शन में सेट हो।

टेस्ट चलाइए

run_tests फंक्शन किसी अभ्यास के टेस्ट चलाने की ज़िम्मेदारी लेता है। जब इस फंक्शन को कॉल किया जाता है, तब तक उदाहरण/एक्ज़ेंप्लर फाइलें (स्टब) हल फाइलों में कॉपी हो चुकी होती हैं, इसलिए आपको बस टेस्ट चलाने के लिए सही कमांड कॉल करनी है।

अगर सारे टेस्ट पास हो जाएँ, तो फंक्शन को एग्ज़िट कोड के रूप में शून्य लौटाना चाहिए, वरना शून्य के अलावा कोई एग्ज़िट कोड लौटाना चाहिए।

Note

run_tests फंक्शन अभ्यास डायरेक्टरी की एक कॉपी पर चलता है, इसलिए फाइलों में जैसा आपको उचित लगे वैसा बदलाव बेझिझक कीजिए।

विकल्प 1: भाषा के टूल का उपयोग कीजिए

अभ्यास जाँचने वाली स्क्रिप्ट के लिए डिफ़ॉल्ट विकल्प यह है कि भाषा के टूल (SDK/बाइनरी/आदि) का उपयोग किया जाए, और ज़्यादातर ट्रैक यही करते हैं। हर ट्रैक का टेस्ट चलाने का अपना तरीका होगा, लेकिन आम तौर पर यह बस एक ही कमांड होती है।

उदाहरण

Arturo ट्रैक की bin/verify-exercises file run_tests फंक्शन को बदलकर टेस्ट फाइल पर सीधे arturo कमांड कॉल करती है:

run_tests() {
    arturo tester.art
}

विकल्प 2: टेस्ट रनर की Docker इमेज का उपयोग कीजिए

दूसरा विकल्प यह है कि ट्रैक के टेस्ट रनर को चलाकर अभ्यासों की जाँच की जाए। यह निश्चित रूप से इस बात पर निर्भर करता है कि ट्रैक के पास काम करने वाला टेस्ट रनर हो।

अगर आपके ट्रैक में अभी टेस्ट रनर नहीं है, तो आप या तो:

  • काम करने वाला टेस्ट रनर बना सकते हैं, या
  • विकल्प 1 अपनाकर सीधे भाषा के टूल का उपयोग कर सकते हैं

डिफ़ॉल्ट bin/verify-exercises स्क्रिप्ट में ये बदलाव करने होंगे:

  1. जाँचिए कि docker कमांड उपलब्ध है
  2. टेस्ट रनर की Docker इमेज पुल (डाउनलोड) कीजिए
  3. हर अभ्यास पर टेस्ट रनर की Docker इमेज चलाने के लिए docker run का उपयोग कीजिए
  4. jq का उपयोग करके जाँचिए कि Docker कंटेनर से लौटी results.json फाइल बताती है कि सारे टेस्ट पास हुए
  5. unskip_test फंक्शन और उस फंक्शन को कॉल करना हटा दीजिए
Note

इस तरीके का सबसे बड़ा फायदा यह है कि यह सबसे अच्छी तरह उसी तरह की नकल करता है जैसे प्रोडक्शन में (वेबसाइट पर) टेस्ट चलाए जाते हैं। इस तरीके से यह संभावना कम हो जाती है कि CI में पास हुई कोई चीज़ प्रोडक्शन में फेल हो। इस तरीके का नुकसान यह है कि यह आम तौर पर धीमा होता है, क्योंकि Docker इमेज पुल करनी पड़ती है और Docker का ओवरहेड भी लगता है।

उदाहरण

Unison ट्रैक की bin/verify-exercises file एक जाँच जोड़ती है ताकि पक्का हो सके कि docker कमांड भी इंस्टॉल है:

required_tool docker

फिर यह ट्रैक की टेस्ट रनर इमेज पुल करती है:

docker pull exercism/unison-test-runner

इसके बाद यह run_tests फंक्शन को बदलती है ताकि docker run से मौजूदा अभ्यास (जो वर्किंग डायरेक्टरी में है) पर टेस्ट रनर चले, और उसके बाद jq कमांड से सही स्टेटस की जाँच हो:

run_tests() {
    local slug

    slug="${1}"

    docker run \
        --rm \
        --network none \
        --mount type=bind,src="${PWD}",dst=/solution \
        --mount type=bind,src="${PWD}",dst=/output \
        --tmpfs /tmp:rw \
        exercism/unison-test-runner "${slug}" "/solution" "/output"
    jq -e '.status == "pass"' "${PWD}/results.json" >/dev/null 2>&1
}

आखिर में, हमें run_tests कमांड को कॉल करने का तरीका बदलना होगा, क्योंकि अब इसे स्लग चाहिए:

run_tests "${slug}"

टेस्ट वर्कफ्लो लागू कीजिए

अब जब verify-exercises स्क्रिप्ट तैयार है, तो test.yml वर्कफ्लो को अंतिम रूप देने का समय है। यह कैसे करना है, यह इस पर निर्भर करता है कि verify-exercises स्क्रिप्ट लागू करने के लिए कौन-सा विकल्प चुना गया था।

विकल्प 1: भाषा के टूल का उपयोग कीजिए

अगर verify-exercises स्क्रिप्ट सीधे भाषा के टूल का उपयोग करती है, तो टेस्ट वर्कफ्लो को ये इंस्टॉल करने होंगे:

  • भाषा के टूल की डिपेंडेंसी, जैसे openssh या कोई C/C++ कंपाइलर।
  • भाषा का टूल, जैसे कोई SDK या बाइनरी। अगर भाषा का टूल इंस्टॉल करने पर इंस्टॉल हुई बाइनरी पाथ में नहीं जुड़ती, तो इसे GitHub Actions के सिस्टम पाथ में जोड़ना न भूलें।

यह हो जाने के बाद verify-exercises अपेक्षा के अनुसार काम करनी चाहिए, और आपने सफलतापूर्वक CI सेट अप कर लिया है!

उदाहरण के लिए, Arturo ट्रैक का test.yml वर्कफ्लो देखिए:

name: Test

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:

jobs:
  ci:
    runs-on: ubuntu-22.04

    steps:
      - name: Checkout repository
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

      - name: Install dependencies
        run: |
          sudo apt-get update
          sudo apt-get install libgtk-3-dev libwebkit2gtk-4.0-dev libmpfr-dev

      - name: Install Arturo
        run: bin/install-arturo
        env:
          GH_TOKEN: ${{ github.token }}

      - name: Verify all exercises
        run: bin/verify-exercises

विकल्प 2: टेस्ट रनर की Docker इमेज का उपयोग कीजिए

दूसरा विकल्प यह है कि ट्रैक के टेस्ट रनर को चलाकर अभ्यासों की जाँच की जाए। इस विकल्प के लिए दो बातें सही होनी चाहिए:

  1. ट्रैक के पास काम करने वाला टेस्ट रनर हो
  2. verify-exercises स्क्रिप्ट किसी अभ्यास के टेस्ट चलाने के लिए टेस्ट रनर की Docker इमेज का उपयोग करती हो

अगर आपके ट्रैक में अभी टेस्ट रनर नहीं है, तो आप या तो:

  • काम करने वाला टेस्ट रनर बना सकते हैं, या
  • विकल्प 1 अपनाकर सीधे भाषा के टूल का उपयोग कर सकते हैं

इस तरीके के कुछ फायदे हैं:

  1. टेस्ट वर्कफ्लो के अंदर आपको कोई डिपेंडेंसी/टूल इंस्टॉल करने की ज़रूरत नहीं पड़ती (क्योंकि वे Docker इमेज के अंदर इंस्टॉल हो चुकी होंगी)
  2. यह तरीका सबसे अच्छी तरह उसी तरह की नकल करता है जैसे प्रोडक्शन में (वेबसाइट पर) टेस्ट चलाए जाते हैं, जिससे प्रोडक्शन में दिक्कतें आने की संभावना कम हो जाती है।

मुख्य नुकसान यह है कि यह शायद धीमा होता है, क्योंकि Docker इमेज पुल करनी पड़ती है और Docker का ओवरहेड भी लगता है।

टेस्ट रनर की Docker इमेज पुल करने के कुछ तरीके हैं:

  1. verify-exercises फाइल के अंदर ही इमेज डाउनलोड करना। Unison ट्रैक यही तरीका अपनाता है।
  2. वर्कफ्लो के अंदर इमेज डाउनलोड करना। Standard ML ट्रैक यही तरीका अपनाता है।
  3. वर्कफ्लो के अंदर इमेज बनाना। 8th ट्रैक यही तरीका अपनाता है।

तो कौन-सा तरीका अपनाना चाहिए? हमारी सलाह है कि कम से कम विकल्प 1 ज़रूर लागू कीजिए, ताकि verify-exercises स्क्रिप्ट स्टैंडअलोन हो जाए। अगर आपकी इमेज खास तौर पर बड़ी है, तो विकल्प 3 भी लागू करना फायदेमंद हो सकता है, जो बनाई गई Docker इमेज को GitHub Actions के कैश में संग्रहीत कर देगा। इसके बाद की बार चलने पर Docker इमेज डाउनलोड करने के बजाय सीधे कैश से पढ़ी जा सकती है, जो परफॉर्मेंस के लिए बेहतर हो सकता है (पक्का करने के लिए खुद नाप-तौल कीजिए)।

विकल्प 3: अभ्यास जाँचने वाली स्क्रिप्ट को टेस्ट रनर की Docker इमेज के अंदर चलाना

एक तीसरा, वैकल्पिक तरीका पिछले दो विकल्पों का मिश्रण है। यहाँ भी हम टेस्ट रनर की Docker इमेज का उपयोग करते हैं, बस इस बार हम verify-exercises स्क्रिप्ट उसी Docker इमेज के अंदर चलाते हैं। इस विकल्प को चालू करने के लिए हमें वर्कफ्लो के कंटेनर को टेस्ट रनर पर सेट करना होगा:

container:
  image: exercism/vimscript-test-runner

इसके बाद हम डिपेंडेंसी और टूल इंस्टॉल करने के चरण छोड़ सकते हैं (क्योंकि वे टेस्ट रनर की Docker इमेज के अंदर इंस्टॉल हो चुके होंगे) और सीधे bin/verify-exercises स्क्रिप्ट चला सकते हैं।

उदाहरण

vimscript ट्रैक का test.yml वर्कफ्लो यह विकल्प अपनाता है:

name: Verify Exercises

on:
  push:
    branches: [main]
  pull_request:
  workflow_dispatch:

jobs:
  ci:
    runs-on: ubuntu-24.04
    container:
      image: exercism/vimscript-test-runner

    steps:
      - name: Checkout repository
        uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332

      - name: Verify all exercises
        run: bin/verify-exercises