Git
Chapters ▾ 2nd Edition

6.5 GitHub - Συγγραφή script στο GitHub

Συγγραφή script στο GitHub

Τώρα λοιπόν έχουμε καλύψει όλες τις σημαντικές λειτουργίες και τις ροές εργασίας του GitHub, αλλά κάθε μεγάλη ομάδα ή έργο θα έχει εξατομικεύσεις που ενδεχομένως θέλουν να κάνουν ή εξωτερικές υπηρεσίες που ενδεχομένως θα ήθελαν να ενσωματώσουν.

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

Άγκιστρα

Η ενότητα Αγκίστρων και Υπηρεσιών της διαχείρισης ενός αποθετηρίου GitHub είναι ο ευκολότερος τρόπος να κάνουμε το GitHub να επικοινωνήσει με εξωτερικά συστήματα.

Υπηρεσίες

Πρώτα θα ρίξουμε μια ματιά στις Υπηρεσίες. Οι ενσωματώσεις τόσο αγκίστρων όσο και υπηρεσιών βρίσκονται στην ενότητα “Settings” του αποθετηρίου, όπου προηγουμένως εξετάσαμε την προσθήκη συνεργατών και την αλλαγή του προεπιλεγμένου κλάδου του έργου μας. Στην καρτέλα “Webhooks and Services” θα δούμε κάτι σαν την Ενότητα ρυθμίσεων υπηρεσιών και αγκίστρων..

Υπηρεσίες και άγκιστρα
Figure 129. Ενότητα ρυθμίσεων υπηρεσιών και αγκίστρων.

Υπάρχουν δεκάδες υπηρεσίες από τις οποίες μπορούμε να επιλέξουμε κάποια, οι περισσότερες από αυτές είναι υπηρεσίες ενσωμάτωσης σε άλλα εμπορικά ή ανοιχτά συστήματα. Οι περισσότερες από αυτές προορίζονται για υπηρεσίες συνεχούς ενσωμάτωσης (continuous integration), παρακολούθησης σφαλμάτων και προβλημάτων, συστήματα χώρων συζήτησης (chat room) και συστήματα τεκμηρίωσης. Θα δούμε βήμα-βήμα τη δημιουργία ενός πολύ απλού αγκίστρου, του αγκίστρου ηλεκτρονικού ταχυδρομείου. Εάν επιλέξουμε “email” από την αναπτυσσόμενη λίστα “Add Service”, θα πάρουμε μια οθόνη διαμόρφωσης όπως η ρύθμιση της υπηρεσίας e-mail..

Ρύθμιση της υπηρεσίας e-mail
Figure 130. ρύθμιση της υπηρεσίας e-mail.

Σε αυτήν την περίπτωση, εάν πατήσουμε το κουμπί “Add service”, η διεύθυνση e-mail που καθορίσαμε θα λάβει ένα μήνυμα e-mail κάθε φορά που κάποιος ωθεί στο αποθετήριο. Οι υπηρεσίες μπορούν να ακούν πολλούς διαφορετικούς τύπους συμβάντων, αλλά οι περισσότερες ακούν μόνο συμβάντα ώθησης και στη συνέχεια κάνουν κάτι με αυτά τα δεδομένα.

Εάν υπάρχει ένα σύστημα που χρησιμοποιούμε που θα θέλαμε να ενσωματώσουμε στο GitHub, θα πρέπει να ελέγξουμε εδώ για να δούμε εάν υπάρχει διαθέσιμη κάποια υπηρεσία ενσωμάτωσης. Για παράδειγμα, αν χρησιμοποιούμε το Jenkins για να εκτελέσουμε δοκιμές στον κώδικά μας, μπορούμε να ενεργοποιήσουμε την ενσωματωμένη στο Jenkins υπηρεσία ενσωμάτωσης για να ξεκινήσουμε μια δοκιμαστική λειτουργία κάθε φορά που κάποιος ωθεί κάτι στο αποθετήριό μας.

Άγκιστρα

Εάν χρειαζόμαστε κάτι πιο συγκεκριμένο ή θέλουμε να ενσωματώσουμε μια υπηρεσία ή έναν ιστότοπο που δεν περιλαμβάνεται σε αυτήν τη λίστα, μπορούμε αντ' αυτού να χρησιμοποιήσουμε το πιο γενικό σύστημα αγκίστρων. Τα άγκιστρα αποθετηρίων στο GitHub είναι αρκετά απλά. Ορίζουμε μια διεύθυνση URL και το GitHub θα αποστείλει ένα ωφέλιμο φορτίο HTTP σε αυτήν τη διεύθυνση URL όποτε συμβαίνει κάποιο συμβάν που θέλουμε.

Γενικά, ο τρόπος με τον οποίο λειτουργεί αυτό είναι ότι μπορούμε να εγκαταστήσουμε μια μικρή υπηρεσία ιστού που να ακούει ένα ωφέλιμο φορτίο αγκίστρων GitHub και στη συνέχεια κάνουμε κάτι με τα δεδομένα όταν παραληφθούν.

Για να ενεργοποιήσουμε ένα άγκιστρο, κάνουμε κλικ στο κουμπί “Add webhook” στο Ενότητα ρυθμίσεων υπηρεσιών και αγκίστρων.. Αυτό θα μας φέρει σε μια σελίδα που μοιάζει με την ρύθμιση αγκίστρων ιστού.

Άγκιστρα ιστού.
Figure 131. ρύθμιση αγκίστρων ιστού

Η διαμόρφωση για ένα άγκιστρο ιστού είναι αρκετά απλή. Στις περισσότερες περιπτώσεις απλά εισάγουμε μια διεύθυνση URL και ένα μυστικό κλειδί και κάνουμε κλικ στο “Add webhook”. Υπάρχουν μερικές επιλογές όσον αφορά στο για ποια συμβάντα θέλουμε το GitHub να μας στείλει ένα ωφέλιμο φορτίο —η προεπιλογή είναι να πάρει μόνο ένα ωφέλιμο φορτίο για το συμβάν push, όταν, δηλαδή, κάποιος ωθήσει νέο κώδικα σε οποιονδήποτε κλάδο του αποθετηρίου μας.

Ας δούμε ένα μικρό παράδειγμα μιας υπηρεσίας ιστού που μπορούμε να στήσουμε για να χειριστούμε ένα άγκιστρο ιστού. Θα χρησιμοποιήσουμε το πλαίσιο ιστού της Ruby, Sinatra, αφού είναι αρκετά περιεκτικό και μπορούμε εύκολα να δούμε τι κάνουμε.

Ας υποθέσουμε ότι θέλουμε να λάβουμε ένα μήνυμα e-mail, αν ένα συγκεκριμένο άτομο ωθήσει σε έναν συγκεκριμένο κλάδο του έργου μας που τροποποιεί ένα συγκεκριμένο αρχείο. Θα μπορούσαμε εύκολα να το κάνουμε με κώδικα όπως αυτός:

require 'sinatra'
require 'json'
require 'mail'

post '/payload' do
  push = JSON.parse(request.body.read) # ανάλυσε το JSON

  # μάζεψε τα δεδομένα που αναζητούμε
  pusher = push["pusher"]["name"]
  branch = push["ref"]

  # πάρε μία λίστα από όλα τα αρχεία που τροποποιήθηκαν
  files = push["commits"].map do |commit|
    commit['added'] + commit['modified'] + commit['removed']
  end
  files = files.flatten.uniq

  # έλεγξε τα κριτήρια
  if pusher == 'schacon' &&
     branch == 'ref/heads/special-branch' &&
     files.include?('special-file.txt')

    Mail.deliver do
      from     'tchacon@example.com'
      to       'tchacon@example.com'
      subject  'Scott Changed the File'
      body     "ALARM"
    end
  end
end

Εδώ παίρνουμε το ωφέλιμο φορτίο JSON που μας παραδίδει το GitHub και αναζητούμε σε αυτό ποιος το ώθησε, σε ποιον κλάδο τον ωθήθηκε και ποια αρχεία είχαν τροποποιηθεί σε όλες τις υποβολές που ωθήθηκαν. Στη συνέχεια αντιπαραβάλλουμε αυτές τις πληροφορίες με τα κριτήρια μας και στέλνουμε ένα μήνυμα e-mail, αν ικανοποιούνται.

Προκειμένου να αναπτύχουμε κα να δοκιμάσουμε κάτι τέτοιο, έχουμε μια ωραία κονσόλα προγραμματιστή στην ίδια οθόνη στην οποία ρυθμίζουμε το άγκιστρο. Μπορούμε να δούμε τις τελευταίες παραδόσεις που προσπάθησε να κάνει το GitHub για αυτό το άγκιστρο ιστού. Για κάθε άγκιστρο μπορούμε να βρούμε ξετρυπώσουμε πότε παραδόθηκε, εάν η παράδοση ήταν επιτυχής καθώς και το σώμα και τις κεφαλίδες τόσο για το αίτημα όσο και για την απάντηση. Αυτό καθιστά απίστευτα εύκολο τον έλεγχο και τον εντοπισμό σφαλμάτων στα άγκιστρά μας.

Εντοπισμός σφαλμάτων σε άγκιστρο ιστού.
Figure 132. Πληροφορίες εντοπισμού σφαλμάτων σε άγκιστρο ιστού.

Το άλλο σπουδαίο χαρακτηριστικό του είναι ότι μπορούμε να ξαναπαραδώσουμε οποιοδήποτε από τα ωφέλιμα φορτία για να δοκιμάσουμε εύκολα την υπηρεσία μας.

Για περισσότερες πληροφορίες σχετικά με τον τρόπο εγγραφής των αγκίστρων ιστού και όλων των διαφορετικών τύπων συμβάντων που μπορούμε να ακούσουμε, μπορούμε να μεταβούμε στην τεκμηρίωση του GitHub Developer στη διεύθυνση: https://developer.github.com/webhooks/

Το API του GitHub

Οι υπηρεσίες και τα άγκιστρα μας δίνουν έναν τρόπο να λαμβάνουμε ειδοποιήσεις ωθήσεων για συμβάντα που συμβαίνουν στα αποθετήριά μα, αλλά τι γίνεται αν χρειαζόμαστε περισσότερες πληροφορίες σχετικά με αυτά τα συμβάντα; Τι γίνεται αν πρέπει να αυτοματοποιήσουμε κάποια ενέργεια όπως την προσθήκη συνεργατών ή επισήμανση ζητημάτων;

Σε αυτές τις περιπτώσεις είναι χρήσιμο το API του GitHub. Το GitHub έχει αμέτρητα σημεία σύνδεσης με το API για να κάνει σχεδόν ο,τιδήποτε μπορούμε να κάνουμε στον ιστότοπο με αυτοματοποιημένο τρόπο. Σε αυτήν την ενότητα θα μάθουμε πώς να ταυτοποιούμαστε και να συνδεόμαστε στο API, πώς να σχολιάζουμε ένα ζήτημα και πώς να αλλάξουμε την κατάσταση ενός αιτήματος έλξης μέσω του API.

Στοιχειώδης χρήση

Το πιο βασικό πράγμα που μπορούμε να κάνουμε είναι ένα απλό αίτημα GET σε ένα σημείο σύνδεσης που δεν απαιτεί ταυτοποίηση. Αυτό θα μπορούσε να είναι πληροφορίες για κάποιον χρήστη ή πληροφορίες μόνο-για-ανάγνωση σε ένα έργο ανοιχτού κώδικα. Για παράδειγμα, εάν θέλουμε να μάθουμε περισσότερα για έναν χρήστη που ονομάζεται schacon, μπορούμε να τρέξουμε κάτι τέτοιο:

$ curl https://api.github.com/users/schacon
{
  "login": "schacon",
  "id": 70,
  "avatar_url": "https://avatars.githubusercontent.com/u/70",
# …
  "name": "Scott Chacon",
  "company": "GitHub",
  "following": 19,
  "created_at": "2008-01-27T17:19:28Z",
  "updated_at": "2014-06-10T02:37:23Z"
}

Υπάρχουν πάρα πολλά τέτοια σημεία σύνδεσης για να λαμβάνουμε πληροφορίες σχετικά με οργανισμούς, έργα, θέματα, υποβολές —σχεδόν για ο,τιδήποτε μπορούμε να δούμε δημοσίως στο GitHub. Μπορούμε ακόμη να χρησιμοποιήσουμε το API για να κάνουμε αποδώσουμε αυθαίρετη Markdown ή να βρούμε ένα πρότυπο .gitignore.

$ curl https://api.github.com/gitignore/templates/Java
{
  "name": "Java",
  "source": "*.class

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.ear

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
"
}

Σχολιασμός θέματος

Ωστόσο, αν θέλουμε να κάνουμε μια ενέργεια στον ιστότοπο, όπως να σχολιάσουμε ένα ζήτημα ή αίτημα έλξης ή αν θέλουμε να προβάλουμε ή να αλληλεπιδράσουμε με ιδιωτικό περιεχόμενο, θα χρειαστεί να επαληθεύσουμε την ταυτότητά μας.

Υπάρχουν πολλοί τρόποι ταυτοποίησης. Μπορούμε να χρησιμοποιήσουμε τη βασική ταυτοποίηση μόνο με το όνομα χρήστη και τον κωδικό πρόσβασής μα, αλλά γενικά είναι καλύτερο να χρησιμοποιήσουμε ένα προσωπικό διακριτικό (token) πρόσβασης. Μπορούμε να δημιουργήσουμε ένα τέτοιο διακριτικό από την καρτέλα “Applications” της σελίδας ρυθμίσεων.

Access Token
Figure 133. Δημιουργία διακριτικού πρόσβασης από την καρτέλα “Applications” της σελίδας ρυθμίσεων.

Θα μας ρωτήσει ποια πεδία εφαρμογής (scopes) θέλουμε για αυτό το διακριτικό και μια περιγραφή. Πρέπει να βεβαιωθούμε ότι χρησιμοποιούμε μια καλή περιγραφή, ώστε να αισθανόμαστε άνετα με την αφαίρεση του διακριτικού όταν το script ή η εφαρμογή μας δεν χρησιμοποιείται πλέον.

Το GitHub θα μας δείξει μόνο μία φορά το διακριτικό, οπότε πρέπει να φροντίσουμε να το αντιγράψουμε. Τώρα μπορούμε να χρησιμοποιήσουμε το διακριτικό για ταυτοποίηση αντί για όνομα χρήστη και κωδικό πρόσβασης. Αυτό είναι ωραίο επειδή μπορούμε να περιορίσουμε το πεδίο εφαρμογής του τι θέλουμε να κάνουμε και το κουπόνι μπορεί να ανακληθεί.

Αυτό έχει επίσης το πρόσθετο πλεονέκτημα ότι αυξάνεται το επιτρεπτό όριο του ρυθμού αιτημάτων. Χωρίς ταυτοποίηση, περιοριζόμαστε σε 60 αιτήματα ανά ώρα. Με ταυτοποίηση μπορούμε να έχουμε μέχρι και 5.000 αιτήματα ανά ώρα.

Ας το χρησιμοποιήσουμε, λοιπόν, για να σχολιάσουμε ένα από τα ζητήματά μας. Ας υποθέσουμε ότι θέλουμε να αφήσουμε ένα σχόλιο σε ένα συγκεκριμένο ζήτημα, το ζήτημα #6. Για να γίνει αυτό, πρέπει να κάνουμε ένα αίτημα HTTP POST στο repos/<χρήστης>/<αποθετήριο>/issues/<αρθ>/comments με το διακριτικό που μόλις δημιουργήσαμε ως το πεδίο Authorization στην κεφαλίδα.

$ curl -H "Content-Type: application/json" \
       -H "Authorization: token TOKEN" \
       --data '{"body":"A new comment, :+1:"}' \
       https://api.github.com/repos/schacon/blink/issues/6/comments
{
  "id": 58322100,
  "html_url": "https://github.com/schacon/blink/issues/6#issuecomment-58322100",
  ...
  "user": {
    "login": "tonychacon",
    "id": 7874698,
    "avatar_url": "https://avatars.githubusercontent.com/u/7874698?v=2",
    "type": "User",
  },
  "created_at": "2014-10-08T07:48:19Z",
  "updated_at": "2014-10-08T07:48:19Z",
  "body": "A new comment, :+1:"
}

Τώρα, αν πάμε σε αυτό το ζήτημα, μπορούμε να δούμε το σχόλιο που μόλις δημοσιεύσαμε όπως στο σχόλιο που αναρτήθηκε από το API του API.

Σχόλιο που αναρτήθηκε από το API του API
Figure 134. σχόλιο που αναρτήθηκε από το API του API

Μπορούμε να χρησιμοποιήσουμε το API για να κάνουμε σχεδόν ο,τιδήποτε μπορούμε να κάνουμε στον ιστότοπο: να δημιουργήσουμε και ορίσουμε ορόσημα (milestones), να αναθέσουμε στους χρήστες ζητήματα και αιτήματα έλξης, να δημιουργήσουμε και αλλάξουμε ετικέτες, να προσβούμε σε δεδομένα υποβολών, να δημιουργήσουμε νέες υποβολές και κλάδους, να ανοίξουμε, κλείσουμε ή συγχωνεύσουμε αιτημάτα έλξης, να δημιουργήσουμε και επεξεργαστούμε ομάδες, να σχολιάσουμε συγκεκριμένες γραμμές κώδικα σε ένα αίτημα έλξης, να κάνουμε αναζητήσεις στη σελίδα και ούτω καθεξής.

Αλλαγή της κατάστασης ενός αιτήματος έλξης

Θα δούμε ένα τελευταίο παράδειγμα, καθώς είναι πραγματικά χρήσιμο, όταν εργαζόμαστε με αιτήματα έλξης. Κάθε υποβολή μπορεί να έχει μία ή περισσότερες καταστάσεις συνδεδεμένες με αυτήν και υπάρχει ένα API για να προσθέσουμε κατάσταση ή να ρωτήσουμε ποια είναι η κατάσταση.

Οι περισσότερες από τις υπηρεσίες συνεχιζούς ενσωμάτωσης και δοκιμών κάνουν χρήση αυτού του API για να αντιδράσουν σε ωθήσεις δοκιμάζοντας τον κώδικα που ωθήθηκε και στη συνέχεια να υποβάλουν αναφορά αν η υποβολή έχει περάσει όλες τις δοκιμές. Θα μπορούσαμε επίσης να το χρησιμοποιήσουμε για να ελέγξουμε αν το μήνυμα υποβολής είναι σωστά μορφοποιημένο, αν αυτός που το υπέβαλλε ακολούθησε όλες τις κατευθυντήριες γραμμές συνεισφορών, εάν η υποβολή ήταν έγκυρα υπογεγραμμένη και άλλα πολλά πράγματα.

Ας υποθέσουμε ότι έχουμε δημιουργήσει ένα άγκιστρο ιστού στο αποθετήριό μας που ενεργοποιεί μια μικρή υπηρεσία ιστού που ελέγχει αν υπάρχει η συμβολοσειρά Signed-off-by στο μήνυμα υποβολής.

require 'httparty'
require 'sinatra'
require 'json'

post '/payload' do
  push = JSON.parse(request.body.read) # ανάλυσε το JSON
  repo_name = push['repository']['full_name']

  # ψάξε μέσα σε κάθε μήνυμα υποβολής
  push["commits"].each do |commit|

    # ψάξε για συμβολοσειρά "Signed-off-by"
    if /Signed-off-by/.match commit['message']
      state = 'success'
      description = 'Successfully signed off!'
    else
      state = 'failure'
      description = 'No signoff found.'
    end

    # post status to GitHub
    sha = commit["id"]
    status_url = "https://api.github.com/repos/#{repo_name}/statuses/#{sha}"

    status = {
      "state"       => state,
      "description" => description,
      "target_url"  => "http://example.com/how-to-signoff",
      "context"     => "validate/signoff"
    }
    HTTParty.post(status_url,
      :body => status.to_json,
      :headers => {
        'Content-Type'  => 'application/json',
        'User-Agent'    => 'tonychacon/signoff',
        'Authorization' => "token #{ENV['TOKEN']}" }
    )
  end
end

Ας ελπίσουμε ότι αυτό είναι αρκετά απλό να ακολουθηθεί. Σε αυτόν τον χειριστή αγκίστρων ιστού εξετάζουμε κάθε υποβολή που μόλις ωθήθηκε, αναζητούμε τη συμβολοσειρά Signed-off-by στο μήνυμα της υποβολής και τέλος κάνουμε POST μέσω HTTP στο σημείο σύνδεσης /repos/<χρήστης>/<αποθετήριο>/statuses/<sha_υποβολής> την κατάσταση.

Σε αυτήν την περίπτωση μπορούμε να στείλουμε μια κατάσταση (επιτυχία, αποτυχία, σφάλμα), μια περιγραφή του τι συνέβη, μια διεύθυνση URL στην οποία μπορεί να μεταβεί ο χρήστης για περισσότερες πληροφορίες και ένα “πλαίσιο” (context) σε περίπτωση που υπάρχουν πολλαπλές καταστάσεις για μία μόνο υποβολή. Για παράδειγμα, μια υπηρεσία δοκιμών μπορεί να παρέχει μια κατάσταση και μια υπηρεσία επικύρωσης, όπως αυτή, μπορεί επίσης να παρέχει μια κατάσταση —το πεδίο “context” είναι εκεί όπου διαφοροποιούνται.

Εάν κάποιος ανοίξει ένα νέο αίτημα έλξης στο GitHub και αυτό το άγκιστρο έχει στηθεί, ίσως δούμε κάτι σαν Κατάσταση υποβολής μέσα από το API..

Κατάσταση υποβολής.
Figure 135. Κατάσταση υποβολής μέσα από το API.

Τώρα μπορούμε να δούμε ένα μικρό πράσινο σημάδι ελέγχου δίπλα στην υποβολή που περιέχει μια συμβολοσειρά Signed-off-by στο μήνυμα και ένα κόκκινο σταυρό σε εκείνο στο οποίο ο συγγραφέας του ξέχασε να υπογράψει. Μπορούμε επίσης να δούμε ότι το αίτημα έλξης την κατάσταση της τελευταίας υποβολής στον κλάδο και μας προειδοποιεί εάν έχει αποτύχει. Αυτό είναι πραγματικά χρήσιμο εάν χρησιμοποιούμε αυτό το API για αποτελέσματα δοκιμών, έτσι ώστε να μην συγχωνεύσουμε κατά λάθος κάτι του οποίου η τελευταία υποβολή είχε αποτύχει.

Octokit

Παρόλο που σε αυτά τα παραδείγματα κάναμε σχεδόν τα πάντα μέσα από την curl και απλά αιτήματα HTTP, υπάρχουν αρκετές βιβλιοθήκες ανοιχτού κώδικα που κάνουν αυτό το API διαθέσιμο με πιο ιδιωματικό τρόπο. Όταν γράφεται αυτό το κείμενο, οι υποστηριζόμενες γλώσσες περιλαμβάνουν τις Go, Objective-C, Ruby και .NET. Για περισσότερες πληροφορίες σχετικά με αυτά, δεδομένου ότι χειρίζονται μεγάλο μέρος του HTTP για εμάς, μπορούμε να ανατρέξουμε στην http://github.com/octokit.

Αυτά τα εργαλεία θα μας βοηθήσουν να εξατομικεύσουμε και να τροποποιήσουμε το GitHub ώστε να λειτουργεί καλύτερα για τις συγκεκριμένες ροές εργασίας μας. Πλήρη τεκμηρίωση σχετικά με ολόκληρο το API καθώς και οδηγούς για κοινές εργασίες, μπρορούμε να βρούμε στην https://developer.github.com.