Ρύθμισε τη συνεχή ενσωμάτωση


Το να ρυθμίσεις τη Συνεχή Ενσωμάτωση (CI) για το track σου είναι πολύ σημαντικό, καθώς βοηθάει στον εντοπισμό λαθών.

GitHub Actions

Τα αποθετήρια του Exercism (συμπεριλαμβανομένων των αποθετηρίων track) χρησιμοποιούν το GitHub Actions για να εκτελούν το CI τους. Το GitHub Actions βασίζεται σε workflows, τα οποία ορίζουν scripts που εκτελούνται αυτόματα κάθε φορά που συμβαίνει ένα συγκεκριμένο γεγονός (π.χ. push ενός commit). Για περισσότερες πληροφορίες σχετικά με τα workflows του GitHub Actions, δες τα έγγραφα για τα workflows.

Προεγκατεστημένα workflows

Τα track έρχονται με προεγκατεστημένα αρκετά workflows, τα περισσότερα από τα οποία δεν πρέπει να τροποποιήσεις (ονομάζονται shared workflows). Υπάρχει όμως ένα workflow που πρέπει να αλλάξεις: το workflow test.yml.

Το workflow των tests

Ο σκοπός του workflow test.yml είναι να επαληθεύει ότι οι ασκήσεις του track βρίσκονται σε σωστή κατάσταση. Το workflow είναι ρυθμισμένο να εκτελείται αυτόματα (στην ορολογία του GitHub Actions: triggered) όταν γίνεται push στο branch main ή στο branch ενός pull request.

Το ίδιο το workflow δεν πρέπει να κάνει πολλά, εκτός από:

  • Checkout του κώδικα (έχει ήδη υλοποιηθεί)
  • Εγκατάσταση εξαρτήσεων (π.χ. εγκατάσταση πακέτων, προαιρετικό)
  • Εγκατάσταση εργαλείων (π.χ. εγκατάσταση ενός SDK, προαιρετικό)
  • Εκτέλεση του script επαλήθευσης ασκήσεων (έχει ήδη υλοποιηθεί)

Υλοποίησε το script επαλήθευσης ασκήσεων

Όπως αναφέρθηκε, οι ασκήσεις επαληθεύονται μέσω ενός script, συγκεκριμένα του script bin/verify-exercises (bash). Αυτό το script είναι σχεδόν έτοιμο και κάνει τα εξής:

  • Κάνει βρόχο σε όλους τους καταλόγους ασκήσεων
  • Για κάθε κατάλογο άσκησης, στη συνέχεια:
    • Αντιγράφει τη λύση example/exemplar στα αρχεία λύσης (stub) (έχει ήδη υλοποιηθεί)
    • Καλεί τη συνάρτηση unskip_tests, στην οποία μπορείς να ενεργοποιήσεις τα tests που είχαν παραλειφθεί στα αρχεία test σου (προαιρετικό)
    • Καλεί τη συνάρτηση run_tests, στην οποία πρέπει να εκτελέσεις τα tests (απαραίτητο)

Οι συναρτήσεις run_tests και unskip_tests είναι τα μόνα που χρειάζεται να υλοποιήσεις.

Ενεργοποίηση των tests που είχαν παραλειφθεί

Αν το track σου υποστηρίζει παράλειψη tests, πρέπει να διασφαλίσουμε ότι κανένα test δεν παραλείπεται κατά την επαλήθευση της λύσης example/exemplar μιας άσκησης. Γενικά, υπάρχουν δύο τρόποι με τους οποίους τα track υποστηρίζουν την "ενεργοποίηση" tests που είχαν παραλειφθεί:

  1. Αφαιρώντας σχολιασμούς/κώδικα/κείμενο από τα αρχεία test. Για παράδειγμα, αλλάζοντας το test.skip σε test.
  2. Παρέχοντας μια μεταβλητή περιβάλλοντος. Για παράδειγμα, ορίζοντας SKIP_TESTS=false.

Αφαίρεση σχολιασμών/κώδικα/κειμένου από τα αρχεία test

Αν η παράλειψη tests βασίζεται σε αρχεία (η πρώτη επιλογή που αναφέρθηκε παραπάνω), τροποποίησε τη συνάρτηση unskip_tests ώστε να αλλάζεις τα αρχεία test (ο υπάρχων κώδικας χειρίζεται ήδη τη διάσχιση των αρχείων test με βρόχο).

Note

Η συνάρτηση unskip_test εκτελείται σε ένα αντίγραφο του καταλόγου μιας άσκησης, οπότε μη διστάσεις να τροποποιήσεις τα αρχεία όπως νομίζεις.

Παράδειγμα

Το bin/verify-exercises file του track Arturo χρησιμοποιεί το sed για να ενεργοποιήσει τα tests μέσα στα αρχεία test:

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

Αν η ενεργοποίηση των tests που είχαν παραλειφθεί απαιτεί να έχει οριστεί μια μεταβλητή περιβάλλοντος, βεβαιώσου ότι ορίζεται στη συνάρτηση run_tests.

Εκτέλεση των tests

Η συνάρτηση run_tests είναι υπεύθυνη για την εκτέλεση των tests μιας άσκησης. Όταν καλείται η συνάρτηση, τα αρχεία example/exemplar θα έχουν ήδη αντιγραφεί στα αρχεία λύσης (stub), οπότε το μόνο που χρειάζεται είναι να καλέσεις τη σωστή εντολή για να εκτελέσεις τα tests.

Η συνάρτηση πρέπει να επιστρέφει μηδέν ως κωδικό εξόδου αν περνούν όλα τα tests, αλλιώς να επιστρέφει μη μηδενικό κωδικό εξόδου.

Note

Η συνάρτηση run_tests εκτελείται σε ένα αντίγραφο του καταλόγου μιας άσκησης, οπότε μη διστάσεις να τροποποιήσεις τα αρχεία όπως νομίζεις.

Επιλογή 1: χρήση των εργαλείων της γλώσσας

Η προεπιλεγμένη επιλογή για το script επαλήθευσης ασκήσεων είναι να χρησιμοποιείς τα εργαλεία της γλώσσας (SDK/binary/κ.λπ.), την οποία χρησιμοποιούν τα περισσότερα track. Κάθε track θα έχει τον δικό του τρόπο να εκτελεί τα tests, αλλά συνήθως είναι απλώς μία εντολή.

Παράδειγμα

Το bin/verify-exercises file του track Arturo τροποποιεί τη συνάρτηση run_tests ώστε να καλεί απλώς την εντολή arturo στο αρχείο test:

run_tests() {
    arturo tester.art
}

Επιλογή 2: χρήση της εικόνας Docker του test runner

Η δεύτερη επιλογή είναι να επαληθεύεις τις ασκήσεις εκτελώντας τον test runner του track. Αυτό βέβαια προϋποθέτει ότι το track έχει έναν λειτουργικό test runner.

Αν το track σου δεν έχει ακόμη test runner, μπορείς είτε:

  • να φτιάξεις έναν λειτουργικό test runner, ή
  • να χρησιμοποιήσεις την επιλογή 1 και να χρησιμοποιήσεις απευθείας τα εργαλεία της γλώσσας

Οι παρακάτω τροποποιήσεις πρέπει να γίνουν στο προεπιλεγμένο script bin/verify-exercises:

  1. Βεβαιώσου ότι η εντολή docker είναι διαθέσιμη
  2. Κάνε pull (λήψη) της εικόνας Docker του test runner
  3. Χρησιμοποίησε το docker run για να εκτελέσεις την εικόνα Docker του test runner σε κάθε άσκηση
  4. Χρησιμοποίησε το jq για να επαληθεύσεις ότι το αρχείο results.json που επιστρέφει το container Docker δείχνει ότι όλα τα tests πέρασαν
  5. Αφαίρεσε τη συνάρτηση unskip_test και την κλήση της
Note

Το βασικό πλεονέκτημα αυτής της προσέγγισης είναι ότι μιμείται καλύτερα τον τρόπο με τον οποίο εκτελούνται τα tests στο παραγωγικό περιβάλλον (στον ιστότοπο). Με αυτή την προσέγγιση, είναι λιγότερο πιθανό να αποτύχουν στην παραγωγή πράγματα που πέρασαν στο CI. Το μειονέκτημα αυτής της προσέγγισης είναι ότι συνήθως είναι πιο αργή, λόγω του ότι πρέπει να κατεβάσεις την εικόνα Docker και της επιβάρυνσης του Docker.

Παράδειγμα

Το bin/verify-exercises file του track Unison προσθέτει τον έλεγχο για να βεβαιωθεί ότι είναι επίσης εγκατεστημένη η εντολή docker:

required_tool docker

Στη συνέχεια, κάνει pull στην εικόνα του test runner του track:

docker pull exercism/unison-test-runner

Έπειτα τροποποιεί τη συνάρτηση run_tests ώστε να χρησιμοποιεί το docker run για να εκτελέσει τον test runner στην τρέχουσα άσκηση (η οποία βρίσκεται στον κατάλογο εργασίας), ακολουθούμενο από μια εντολή 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}"

Υλοποίησε το workflow των tests

Τώρα που το script verify-exercises ολοκληρώθηκε, ήρθε η ώρα να οριστικοποιήσεις το workflow test.yml. Ο τρόπος εξαρτάται από την επιλογή που διάλεξες για την υλοποίηση του script verify-exercises.

Επιλογή 1: χρήση των εργαλείων της γλώσσας

Αν το script verify-exercises χρησιμοποιεί απευθείας τα εργαλεία της γλώσσας, το workflow των tests θα πρέπει να εγκαταστήσει:

  • Εξαρτήσεις των εργαλείων της γλώσσας, όπως το openssh ή έναν μεταγλωττιστή C/C++.
  • Τα εργαλεία της γλώσσας, όπως ένα SDK ή binary. Αν η εγκατάσταση των εργαλείων της γλώσσας δεν προσθέτει το/τα εγκατεστημένα binary στο path, φρόντισε να το προσθέσεις στο system path του GitHub Actions.

Μόλις γίνει αυτό, το verify-exercises θα πρέπει να λειτουργεί όπως αναμένεται, και θα έχεις ρυθμίσει επιτυχώς το CI!

Για ένα παράδειγμα, δες το workflow test.yml του track Arturo:

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 του test runner

Η δεύτερη επιλογή είναι να επαληθεύεις τις ασκήσεις εκτελώντας τον test runner του track. Αυτή η επιλογή απαιτεί να ισχύουν δύο πράγματα:

  1. Το track να έχει έναν λειτουργικό test runner
  2. Το script verify-exercises να χρησιμοποιεί την εικόνα Docker του test runner για να εκτελεί τα tests μιας άσκησης

Αν το track σου δεν έχει ακόμη test runner, μπορείς είτε:

  • να φτιάξεις έναν λειτουργικό test runner, ή
  • να χρησιμοποιήσεις την επιλογή 1 και να χρησιμοποιήσεις απευθείας τα εργαλεία της γλώσσας

Αυτή η προσέγγιση έχει μερικά πλεονεκτήματα:

  1. Δε χρειάζεται να εγκαταστήσεις εξαρτήσεις/εργαλεία μέσα στο workflow των tests (καθώς αυτά θα έχουν εγκατασταθεί μέσα στην εικόνα Docker)
  2. Η προσέγγιση μιμείται καλύτερα τον τρόπο με τον οποίο εκτελούνται τα tests στο παραγωγικό περιβάλλον (στον ιστότοπο), μειώνοντας την πιθανότητα προβλημάτων στην παραγωγή.

Το βασικό μειονέκτημα είναι ότι πιθανώς είναι πιο αργή, λόγω του ότι πρέπει να κατεβάσεις την εικόνα Docker και της επιβάρυνσης του Docker.

Υπάρχουν μερικοί τρόποι με τους οποίους μπορείς να κατεβάσεις την εικόνα Docker του test runner:

  1. Να κατεβάσεις την εικόνα μέσα στο αρχείο verify-exercises. Αυτή είναι η προσέγγιση που ακολουθεί το track Unison.
  2. Να κατεβάσεις την εικόνα μέσα στο workflow. Αυτή είναι η προσέγγιση που ακολουθεί το track Standard ML.
  3. Να χτίσεις την εικόνα μέσα στο workflow. Αυτή είναι η προσέγγιση που ακολουθεί το track 8th.

Ποια προσέγγιση να χρησιμοποιήσεις, λοιπόν; Συνιστούμε να υλοποιήσεις τουλάχιστον την επιλογή 1, ώστε το script verify-exercises να είναι αυτόνομο. Αν η εικόνα σου είναι ιδιαίτερα μεγάλη, μπορεί να είναι ωφέλιμο να υλοποιήσεις και την επιλογή 3, η οποία θα αποθηκεύσει την εικόνα Docker που έχτισες στην cache του GitHub Actions. Οι επόμενες εκτελέσεις μπορούν τότε απλώς να διαβάζουν την εικόνα Docker από την cache, αντί να την κατεβάζουν, κάτι που μπορεί να είναι καλύτερο για την απόδοση (μέτρησέ το για να είσαι σίγουρος).

Επιλογή 3: εκτέλεση του script επαλήθευσης ασκήσεων μέσα στην εικόνα Docker του test runner

Μια τρίτη, εναλλακτική επιλογή είναι ένας υβριδικός συνδυασμός των δύο προηγούμενων επιλογών. Εδώ χρησιμοποιούμε επίσης την εικόνα Docker του test runner, μόνο που αυτή τη φορά εκτελούμε το script verify-exercises μέσα σε αυτή την εικόνα Docker. Για να ενεργοποιήσεις αυτή την επιλογή, πρέπει να ορίσουμε το container του workflow στον test runner:

container:
  image: exercism/vimscript-test-runner

Μπορούμε τότε να παραλείψουμε τα βήματα εγκατάστασης εξαρτήσεων και εργαλείων (καθώς αυτά θα έχουν εγκατασταθεί μέσα στην εικόνα Docker του test runner) και να προχωρήσουμε στην εκτέλεση του script bin/verify-exercises.

Παράδειγμα

Το workflow test.yml του track vimscript χρησιμοποιεί αυτή την επιλογή:

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