Μετάβαση στο περιεχόμενο

Pipeline και αυτοματισμός

Τα pipelines είναι το ντετερμινιστικό μισό του Control Center. Ένας πράκτορας αποφασίζει τι θα πει· ένα pipeline αποφασίζει τι συμβαίνει μετά, με τον ίδιο τρόπο κάθε φορά. Ένα pipeline είναι ένα DAG από βήματα που μπορούν να στέλνουν πράκτορες, να εκτελούν σενάρια, να διακλαδίζονται με βάση μια συνθήκη, να απλώνονται σε μια συλλογή και να περνούν δεδομένα μεταξύ βημάτων — με μόνιμη αποθήκευση, δυνατότητα συνέχισης και κοστολόγηση.

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

Ένα πρότυπο (PipelineDefinition) είναι το δηλωτικό DAG: βήματα, δηλωμένες είσοδοι και διαμόρφωση. Ζει στη βάση δεδομένων του ίδιου του χώρου εργασίας. Μια εκτέλεση (PipelineRun) είναι μία μεμονωμένη εκτέλεση αυτού του προτύπου, που μεταφέρει ένα μεταβλητό δοχείο κατάστασης, σύνολα κόστους και tokens και κατάσταση ανά βήμα.

Ένα πρότυπο έχει πολλές εκτελέσεις. Οι εκτελέσεις επιβιώνουν από μια επανεκκίνηση: ο μηχανισμός φορτώνει ξανά τις εκτελέσεις που βρίσκονται σε εξέλιξη και τις συνεχίζει από εκεί που σταμάτησαν.

Δύο δικλείδες ασφαλείας αποτυγχάνουν σκόπιμα ολόκληρη την εκτέλεση αντί να συνεχίσουν ένα βήμα, επειδή η συνέχιση θα ήταν χειρότερη από τη διακοπή:

  • Ένα βήμα που βρίσκεται σε κατάσταση suspended για περισσότερο από suspendedStepTimeout (24 ώρες από προεπιλογή) περιμένει εισιτήρια που δεν θα φτάσουν ποτέ σε τελική κατάσταση, οπότε αποτυγχάνει για να ελευθερωθεί η εκτέλεση. Η εξαίρεση είναι το extras['awaitApproval'] == true — δηλαδή η πύλη μερικής έγκρισης orchestration.awaitApproval. Το human.gate δεν εξαιρείται.
  • Ένα βήμα που έχει δηλωθεί με extras: {'idempotent': false} και βρισκόταν σε εξέλιξη όταν σταμάτησε ο διακομιστής μπορεί να έχει ήδη εφαρμόσει την παρενέργειά του (ένα squash merge, έναν καθαρισμό worktree). Η επανεκτέλεσή του θα μπορούσε να την εφαρμόσει δύο φορές, οπότε ο μηχανισμός το αποτυγχάνει και αφήνει την επανάληψη σε εσάς.

Το είδος προγραμματίζει, το σώμα εκτελεί

Ενότητα με τίτλο «Το είδος προγραμματίζει, το σώμα εκτελεί»

Ένα βήμα έχει δύο ορθογώνια μέρη και ο διαχωρισμός τους είναι αυτό που κρατά τον μηχανισμό μικρό.

Το είδος (StepKind) λέει στον μηχανισμό πότε ένα βήμα γίνεται εκτελέσιμο σε σχέση με τα ανάντη βήματα — αυτό είναι το μόνο που χρειάζεται να γνωρίζει ο χρονοπρογραμματιστής. Υπάρχουν έξι: trigger, listen, join, router, forEach και terminal. Το σώμα (bodyKey) είναι η εργασία που εκτελεί το βήμα μόλις προγραμματιστεί. Τα σώματα είναι κλεισίματα που καταχωρίζονται με κλειδί κατά την εκκίνηση του διακομιστή, οπότε πολλά βήματα διαφορετικών ειδών μπορούν να μοιράζονται ένα σώμα: για παράδειγμα, πολλά βήματα listen που εκτελούν όλα το conversation.promptAgent.

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

Δύο συνέπειες του σχεδιασμού με μητρώο αξίζει να τις γνωρίζετε πριν συντάξετε ένα πρότυπο με το χέρι:

  • Ένα βήμα του οποίου το bodyKey δεν είναι καταχωρισμένο παράγει μια προειδοποίηση κατά τη φόρτωση του προτύπου και έπειτα δεν εκτελείται ποτέ. Τίποτα δεν αποτυγχάνει με εμφανή τρόπο κατά την εκτέλεση. Τα βήματα trigger και terminal εξαιρούνται από αυτή την προειδοποίηση: το έναυσμα εκτελεί και πάλι το σώμα pipeline.trigger που δεν κάνει τίποτα, ώστε να ενεργοποιηθούν οι κατάντη ακροατές· ένα βήμα terminal δεν έχει σώμα και είναι φρουρός του χρονοπρογραμματιστή.
  • Τα κλειδιά στον χάρτη extras ενός κόμβου απαιτούν ακριβή αντιστοίχιση και αγνοούνται σιωπηλά σε κάθε άλλη περίπτωση. Μόνο το extras['spaceId'] τοποθετεί την εργασία ενός βήματος πράκτορα σε υπάρχουσα συνομιλία. Το extras['mode'] (chat / plan / review) διαβάζεται στον κόμβο που ανοίγει τον χώρο (messaging.createSpace, προεπιλογή review) — ένα μεταγενέστερο βήμα πράκτορα δεν μπορεί να αλλάξει εκ νέου τα δικαιώματα μιας συνομιλίας στην οποία έχει μπει. Οποιαδήποτε άλλη γραφή οποιουδήποτε από τα δύο κλειδιά αγνοείται.

Τα βήματα δεν καλούν το ένα το άλλο. Διαβάζουν και γράφουν σε ένα κοινό δοχείο κατάστασης της εκτέλεσης: ένα βήμα διαβάζει τις εισόδους του με κλειδί, εκτελείται και γράφει την έξοδό του πίσω κάτω από το δικό του outputKey. Ένα StateReducer συγχωνεύει τις ταυτόχρονες εγγραφές όταν παράλληλοι κλάδοι στοχεύουν το ίδιο κλειδί και ένα TemplateRenderer αντικαθιστά τα σύμβολα θέσης {{...}} σε προτροπές και σενάρια από αυτή την ίδια κατάσταση.

Γι’ αυτό ένα pipeline μπορεί να επιθεωρηθεί εκ των υστέρων — το δοχείο κατάστασης είναι όλη η ιστορία της εκτέλεσης.

Οι εγγραφές PipelineTrigger δηλώνουν πότε ένα πρότυπο ξεκινά αυτόματα. Υπάρχουν τέσσερα είδη:

Έναυσμα Ξεκινά μια εκτέλεση όταν
Γεγονός τομέα Ενεργοποιείται ένα αντίστοιχο γεγονός τομέα — για παράδειγμα PullRequestPublished ή TicketAssigned
Χρονοδιάγραμμα Μια έκφραση cron ή διαστήματος φτάνει στην ώρα εκτέλεσής της
Χειροκίνητα Την εκτελείτε με το χέρι· η ύπαρξη ενεργοποιημένου χειροκίνητου εναύσματος είναι αυτό που εμφανίζει το πρότυπο στον επιλογέα εκτέλεσης
Webhook Ένα εξωτερικό σύστημα κάνει POST στο /webhooks/<token> στη θύρα HTTP του διακομιστή

Τα εναύσματα γεγονότων αποτελούν κλειστό σύνολο: 16 τύποι γεγονότων είναι γνωστοί στον διανομέα και προσφέρονται στον επιλογέα (EventPayloadMapper.knownEventTypes) και τίποτα άλλο δεν μπορεί να ξεκινήσει ένα pipeline. Δεκατέσσερα από αυτά τα 16 έχουν αντιστοίχιση ωφέλιμου φορτίου. Δύο — τα TicketCreated και TicketStatusChanged — δεν έχουν καμία, οπότε ένα έναυσμα σε οποιοδήποτε από τα δύο μένει ενεργό και δεν ενεργοποιείται ποτέ. Δείτε τα Γεγονότα τομέα για τον πλήρη κατάλογο.

Το token του webhook χρησιμεύει ταυτόχρονα και ως μυστικό HMAC: η κεφαλίδα X-Hub-Signature-256: sha256=<hex> του αιτήματος επαληθεύεται έναντι του HMAC-SHA256(body, token), οι μη επαληθευμένες παραδόσεις απορρίπτονται και δεν είναι δυνατή η επανάληψή τους, ενώ τα διπλότυπα απορρίπτονται από το dedupe_key. Επειδή οι εγγραφές εναυσμάτων είναι μοναδικές ως προς (χώρος εργασίας, τύπος γεγονότος, πρότυπο), ένα πρότυπο μπορεί να έχει το πολύ ένα έναυσμα webhook και ένα έναυσμα ανά τύπο γεγονότος τομέα.

Τα ενσωματωμένα πρότυπα αρχικοποιούν ενεργοποιημένα τα χειροκίνητα εναύσματα και τα εναύσματα γεγονότων· τα προγραμματισμένα εναύσματα αρχικοποιούνται απενεργοποιημένα, οπότε μια επαναλαμβανόμενη σάρωση είναι πάντα προαιρετική.

Τα πρότυπα που παραδίδονται απενεργοποιημένα

Ενότητα με τίτλο «Τα πρότυπα που παραδίδονται απενεργοποιημένα»

Δεκατρία πρότυπα pipeline αρχικοποιούνται σε κάθε χώρο εργασίας και οκτώ από αυτά παραδίδονται απενεργοποιημένα: external_pr_welcome, cross_review, ticket_to_pr, pr_triage, pre_merge_gate, release_notes, dep_audit και pr_digest.

Αυτό είναι το σημείο που εκπλήσσει τους περισσότερους. Τα εναύσματά τους αρχικοποιούνται ενεργοποιημένα, αλλά το PipelineEngine.start αρνείται ένα απενεργοποιημένο πρότυπο και επιστρέφει null — έτσι το έναυσμα ενεργοποιείται στο κενό και δεν εμφανίζεται καμία εκτέλεση. Ο διακόπτης είναι το πρότυπο, όχι το έναυσμα. Ενεργοποιήστε το στις Ρυθμίσεις → Χώρος εργασίας → Πρότυπα pipeline πριν περιμένετε να κάνει οτιδήποτε οποιοδήποτε από αυτά τα οκτώ.

Η εκ νέου αρχικοποίηση ενός χώρου εργασίας διατηρεί ό,τι κι αν έχετε επιλέξει: η ρύθμιση isEnabled και οι υπάρχουσες εγγραφές εναυσμάτων σας επιβιώνουν. Υπάρχει ακριβώς μία σκόπιμη εξαίρεση — το ημερήσιο χρονοδιάγραμμα του pr_merged_cleanup ενεργοποιείται αναγκαστικά ακόμη και αν ήταν προηγουμένως κλειστό, επειδή αποτελεί συλλογή απορριμμάτων. Χωρίς αυτό συσσωρεύονται εγγραφές worktree, κατατμήσεις του γραφήματος κώδικα και αντίγραφα copy-on-write, και αυτό δεν είχε ποτέ σκοπό να αποτελεί επιλογή του χρήστη.

Μια ακόμη ασυμμετρία αξίζει να τη γνωρίζετε: το ενσωματωμένο pr_review δεν δημοσιεύει πλέον σκόπιμα στο GitHub. Η δημοσίευση ενός ελέγχου είναι ξεχωριστό βήμα που περνά από έγκριση του χρήστη και ένα pipeline που δημοσίευε απευθείας θα δημοσίευε δύο φορές. Τα cross_review, ticket_to_pr και pr_triage εξακολουθούν να δημοσιεύουν σχόλιο απευθείας στο GitHub όταν εκτελούνται.

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

Αυτό που δεν γνωρίζει είναι οτιδήποτε σχετίζεται με εισιτήρια, αποστολή, GitHub ή μηνύματα. Η μόνη του ραφή επέκτασης είναι το PipelineBodyRegistry. Τα σώματα που είναι καταχωρισμένα εκεί κρατούν τα AgentDispatchPort, MessagingPort και τα συγγενικά τους — γι’ αυτό η προσθήκη μιας δυνατότητας στα pipelines δεν σημαίνει ποτέ αλλαγή του χρονοπρογραμματιστή.

Ένα βήμα που αποτυγχάνει καταγράφει το μήνυμα σφάλματος και μεταβαίνει σε failed. Αν το βήμα δηλώνει continueOnFail, τα κατάντη βήματα προχωρούν και το σφάλμα αποθηκεύεται στην κατάσταση της εκτέλεσης· διαφορετικά η εκτέλεση αποτυγχάνει. Ένα βήμα με StepRetryPolicy επαναλαμβάνεται πρώτα, με γραμμική ή εκθετική υποχώρηση, πριν εφαρμοστεί οποιοδήποτε από τα παραπάνω.