কন্টিনিউয়াস ইন্টিগ্রেশন সেটআপ করুন


আপনার ট্র্যাকের জন্য Continuous Integration (CI) সেট আপ করা খুবই গুরুত্বপূর্ণ, কারণ এটি ভুল ধরতে সাহায্য করে।

GitHub Actions

Exercism-এর রিপোজিটরিগুলো (ট্র্যাক রিপোজিটরিসহ) তাদের CI চালাতে GitHub Actions ব্যবহার করে। GitHub Actions নির্ভর করে ওয়ার্কফ্লো-এর উপর, যেগুলো কোনো নির্দিষ্ট ঘটনা ঘটলে (যেমন একটি কমিট পুশ করা) স্বয়ংক্রিয়ভাবে চালানোর স্ক্রিপ্ট নির্ধারণ করে। GitHub Actions ওয়ার্কফ্লো সম্পর্কে আরও জানতে, ওয়ার্কফ্লো ডকুমেন্টেশন দেখুন।

আগে থেকে ইনস্টল করা ওয়ার্কফ্লো

ট্র্যাকগুলোতে আগে থেকেই বেশ কিছু ওয়ার্কফ্লো ইনস্টল করা থাকে, যেগুলোর বেশিরভাগই আপনার পরিবর্তন করা উচিত নয় (এগুলোকে শেয়ার্ড ওয়ার্কফ্লো বলা হয়)। তবে একটি ওয়ার্কফ্লো আছে যা আপনার পরিবর্তন করা উচিত, সেটি হলো test.yml ওয়ার্কফ্লো।

টেস্ট ওয়ার্কফ্লো

test.yml ওয়ার্কফ্লোর লক্ষ্য হলো ট্র্যাকের অনুশীলনীগুলো সঠিক অবস্থায় আছে কি না, তা যাচাই করা। main ব্রাঞ্চে বা কোনো পুল রিকোয়েস্টের ব্রাঞ্চে পুশ করা হলে এই ওয়ার্কফ্লো স্বয়ংক্রিয়ভাবে চালু হওয়ার জন্য সেট আপ করা থাকে (GitHub Actions-এর পরিভাষায়: triggered হয়)।

ওয়ার্কফ্লোর নিজের খুব বেশি কাজ থাকা উচিত নয়, শুধু এগুলো ছাড়া:

  • কোড চেকআউট করা (আগেই করা আছে)
  • ডিপেন্ডেন্সি ইনস্টল করা (যেমন প্যাকেজ ইনস্টল করা, ঐচ্ছিক)
  • টুলিং ইনস্টল করা (যেমন একটি SDK ইনস্টল করা, ঐচ্ছিক)
  • verify exercises স্ক্রিপ্ট চালানো (আগেই করা আছে)

verify exercises স্ক্রিপ্টটি তৈরি করুন

যেমন বলা হয়েছে, অনুশীলনীগুলো একটি স্ক্রিপ্ট দিয়ে যাচাই করা হয়, সেটি হলো bin/verify-exercises (bash) স্ক্রিপ্ট। এই স্ক্রিপ্টটি প্রায় তৈরি হয়ে গেছে, এবং এটি নিচের কাজগুলো করে:

  • সব অনুশীলনী ডিরেক্টরির উপর লুপ করে
  • প্রতিটি অনুশীলনী ডিরেক্টরির জন্য এটি তখন:
    • example/exemplar সমাধানটি (স্টাব) সমাধান ফাইলে কপি করে (আগেই করা আছে)
    • unskip_tests ফাংশন কল করে, যেখানে আপনি আপনার টেস্ট ফাইলের টেস্টগুলো আনস্কিপ করতে পারেন (ঐচ্ছিক)
    • run_tests ফাংশন কল করে, যেখানে আপনার টেস্টগুলো রান করা উচিত (আবশ্যক)

শুধু run_tests আর unskip_tests ফাংশন দুটোই আপনার তৈরি করা প্রয়োজন।

টেস্ট আনস্কিপ করা

আপনার ট্র্যাক যদি টেস্ট স্কিপ করা সাপোর্ট করে, তবে কোনো অনুশীলনীর example/exemplar সমাধান যাচাই করার সময় যেন কোনো টেস্ট স্কিপ না হয়, তা নিশ্চিত করতে হবে। সাধারণত, ট্র্যাকগুলো টেস্ট "আনস্কিপ" করার দুটি উপায় সাপোর্ট করে:

  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 ফাংশনটি একটি অনুশীলনীর টেস্ট রান করার দায়িত্বে থাকে। ফাংশনটি কল করা হলে, example/exemplar ফাইলগুলো ইতিমধ্যেই (স্টাব) সমাধান ফাইলে কপি হয়ে যাবে, তাই টেস্টগুলো রান করতে শুধু সঠিক কমান্ডটি কল করলেই হবে।

সব টেস্ট পাস হলে ফাংশনটিকে exit code হিসেবে শূন্য রিটার্ন করতে হবে, অন্যথায় শূন্য নয় এমন একটি exit code রিটার্ন করতে হবে।

Note

run_tests ফাংশনটি একটি অনুশীলনী ডিরেক্টরির একটি কপিতে চলে, তাই আপনার ইচ্ছেমতো ফাইলগুলো পরিবর্তন করতে পারেন।

বিকল্প ১: ভাষার টুলিং ব্যবহার করা

verify exercises স্ক্রিপ্টের ডিফল্ট বিকল্প হলো ভাষার টুলিং (SDK/বাইনারি/ইত্যাদি) ব্যবহার করা, যা বেশিরভাগ ট্র্যাক ব্যবহার করে। প্রতিটি ট্র্যাকের টেস্ট রান করার নিজস্ব উপায় থাকবে, তবে সাধারণত এটি শুধু একটি একক কমান্ড।

উদাহরণ

Arturo ট্র্যাকের bin/verify-exercises file run_tests ফাংশনটি পরিবর্তন করে যাতে এটি টেস্ট ফাইলে সরাসরি arturo কমান্ড কল করে:

run_tests() {
    arturo tester.art
}

বিকল্প ২: টেস্ট রানার Docker ইমেজ ব্যবহার করা

দ্বিতীয় বিকল্প হলো ট্র্যাকের টেস্ট রানার চালিয়ে অনুশীলনীগুলো যাচাই করা। এটি অবশ্যই নির্ভর করে ট্র্যাকের একটি কার্যকর টেস্ট রানার থাকার উপর।

আপনার ট্র্যাকের যদি এখনো টেস্ট রানার না থাকে, তবে আপনি দুটির একটি করতে পারেন:

  • একটি কার্যকর টেস্ট রানার তৈরি করা, অথবা
  • বিকল্প ১ নিয়ে সরাসরি ভাষার টুলিং ব্যবহার করা

ডিফল্ট bin/verify-exercises স্ক্রিপ্টে নিচের পরিবর্তনগুলো আনতে হবে:

  1. docker কমান্ডটি পাওয়া যায় কি না, তা যাচাই করা
  2. টেস্ট রানার Docker ইমেজ পুল (ডাউনলোড) করা
  3. প্রতিটি অনুশীলনীতে টেস্ট রানার Docker ইমেজ চালাতে docker run ব্যবহার করা
  4. Docker কন্টেইনার থেকে পাওয়া results.json ফাইলে সব টেস্ট পাসের কথা বোঝা যাচ্ছে কি না, তা যাচাই করতে jq ব্যবহার করা
  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 কমান্ডের কলটি পরিবর্তন করতে হবে, কারণ এখন এটি slug চেয়ে থাকে:

run_tests "${slug}"

টেস্ট ওয়ার্কফ্লোটি তৈরি করুন

এখন যেহেতু verify-exercises স্ক্রিপ্ট শেষ, সময় হয়েছে test.yml ওয়ার্কফ্লোটি চূড়ান্ত করার। এটি কীভাবে করবেন, তা নির্ভর করে verify-exercises স্ক্রিপ্টের বাস্তবায়নে কোন বিকল্প বেছে নেওয়া হয়েছে তার উপর।

বিকল্প ১: ভাষার টুলিং ব্যবহার করা

verify-exercises স্ক্রিপ্ট যদি সরাসরি ভাষার টুলিং ব্যবহার করে, তবে টেস্ট ওয়ার্কফ্লোকে ইনস্টল করতে হবে:

  • ভাষার টুলিংয়ের ডিপেন্ডেন্সি, যেমন openssh বা একটি C/C++ কম্পাইলার।
  • ভাষার টুলিং, যেমন একটি SDK বা বাইনারি। ভাষার টুলিং ইনস্টলেশন যদি ইনস্টল করা বাইনারি/বাইনারীগুলোকে path-এ যোগ না করে, তবে অবশ্যই এটি GitHub Actions-এর সিস্টেম path-এ যোগ করুন।

এটি হয়ে গেলে, 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

বিকল্প ২: টেস্ট রানার Docker ইমেজ ব্যবহার করা

দ্বিতীয় বিকল্প হলো ট্র্যাকের টেস্ট রানার চালিয়ে অনুশীলনীগুলো যাচাই করা। এই বিকল্পের জন্য দুটি শর্ত পূরণ হতে হবে:

  1. ট্র্যাকের একটি কার্যকর টেস্ট রানার আছে
  2. verify-exercises স্ক্রিপ্ট একটি অনুশীলনীর টেস্ট চালাতে টেস্ট রানার Docker ইমেজ ব্যবহার করে

আপনার ট্র্যাকের যদি এখনো টেস্ট রানার না থাকে, তবে আপনি দুটির একটি করতে পারেন:

  • একটি কার্যকর টেস্ট রানার তৈরি করা, অথবা
  • বিকল্প ১ নিয়ে সরাসরি ভাষার টুলিং ব্যবহার করা

এই পদ্ধতির কয়েকটি সুবিধা আছে:

  1. টেস্ট ওয়ার্কফ্লোর মধ্যে আপনাকে কোনো ডিপেন্ডেন্সি/টুলিং ইনস্টল করতে হবে না (কারণ সেগুলো Docker ইমেজের ভেতরে ইনস্টল করা থাকবে)
  2. এই পদ্ধতি প্রোডাকশনে (ওয়েবসাইটে) টেস্ট কীভাবে রান হয় তা সবচেয়ে ভালোভাবে অনুকরণ করে, ফলে প্রোডাকশনে সমস্যা হওয়ার সম্ভাবনা কমে।

প্রধান অসুবিধা হলো, Docker ইমেজ পুল করতে হওয়া এবং Docker-এর বাড়তি ওভারহেডের কারণে এটি সম্ভবত ধীর হয়।

টেস্ট রানার Docker ইমেজ পুল করার কয়েকটি উপায় আছে:

  1. verify-exercises ফাইলের ভেতরেই ইমেজটি ডাউনলোড করা। এই পদ্ধতি নিয়েছে Unison ট্র্যাক।
  2. ওয়ার্কফ্লোর ভেতরে ইমেজটি ডাউনলোড করা। এই পদ্ধতি নিয়েছে Standard ML ট্র্যাক।
  3. ওয়ার্কফ্লোর ভেতরে ইমেজটি বিল্ড করা। এই পদ্ধতি নিয়েছে 8th ট্র্যাক।

তাহলে কোন পদ্ধতি ব্যবহার করবেন? আমরা সুপারিশ করি অন্ততপক্ষে ১ নম্বর বিকল্পটি বাস্তবায়ন করতে, যাতে verify-exercises স্ক্রিপ্টটি স্ট্যান্ডঅ্যালোন হয়। আপনার ইমেজ যদি বিশেষভাবে বড় হয়, তবে ৩ নম্বর বিকল্পটিও বাস্তবায়ন করা উপকারী হতে পারে, যা বিল্ড করা Docker ইমেজ GitHub Actions ক্যাশে জমা রাখবে। তখন পরবর্তী রানগুলো ইমেজ ডাউনলোড না করে শুধু ক্যাশ থেকে পড়তে পারবে, যা পারফরম্যান্সের জন্য ভালো হতে পারে (নিশ্চিত হতে মেপে দেখুন)।

বিকল্প ৩: টেস্ট রানার Docker ইমেজের ভেতরে verify exercises স্ক্রিপ্ট চালানো

তৃতীয় একটি বিকল্প উপায় হলো আগের দুটি বিকল্পের মিশ্রণ। এখানেও আমরা টেস্ট রানার Docker ইমেজ ব্যবহার করছি, তবে এবার verify-exercises স্ক্রিপ্টটি সেই Docker ইমেজের ভেতরেই চালাই। এই বিকল্প চালু করতে, ওয়ার্কফ্লোর container-কে টেস্ট রানারে সেট করতে হবে:

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