Implementați automat aplicații web folosind webhook-uri GitHub

Publicat: 2022-03-11

Oricine dezvoltă aplicații web și încearcă să le ruleze pe propriile servere negestionate este conștient de procesul obositor implicat în implementarea aplicației și în avansarea actualizărilor viitoare. Furnizorii de platformă ca serviciu (PaaS) au facilitat implementarea aplicațiilor web fără a fi nevoie să treacă prin procesul de furnizare și configurare a serverelor individuale, în schimbul unei creșteri ușoare a costurilor și unei scăderi a flexibilității. Este posibil ca PaaS să fi făcut lucrurile mai ușoare, dar uneori avem nevoie sau vrem să implementăm aplicații pe propriile servere neadministrate. Automatizarea acestui proces de implementare a aplicațiilor web pe serverul dvs. poate părea copleșitoare la început, dar, în realitate, a veni cu un instrument simplu pentru a automatiza acest lucru poate fi mai ușor decât credeți. Cât de ușor va fi să implementați acest instrument depinde în mare măsură de cât de simple sunt nevoile dvs., dar cu siguranță nu este dificil de realizat și poate ajuta, probabil, să economisiți mult timp și efort făcând piese repetitive plictisitoare ale aplicației web. implementari.

Mulți dezvoltatori au venit cu propriile modalități de automatizare a proceselor de implementare a aplicațiilor lor web. Deoarece modul în care implementați aplicațiile dvs. web depinde foarte mult de tehnologia exactă utilizată, aceste soluții de automatizare variază între ele. De exemplu, pașii implicați în implementarea automată a unui site web PHP sunt diferiți de implementarea unei aplicații web Node.js. Există și alte soluții, cum ar fi Dokku, care sunt destul de generice și aceste lucruri (numite buildpack-uri) funcționează bine cu o gamă mai largă de tehnologie.

aplicații web și webhook-uri

În acest tutorial, vom arunca o privire asupra ideilor fundamentale din spatele unui instrument simplu pe care îl puteți construi pentru a automatiza implementările aplicațiilor dvs. web folosind webhook-uri GitHub, buildpack-uri și Procfiles. Codul sursă al programului prototip pe care îl vom explora în acest articol este disponibil pe GitHub.

Noțiuni introductive cu aplicațiile web

Pentru a automatiza implementarea aplicației noastre web, vom scrie un program Go simplu. Dacă nu sunteți familiarizat cu Go, nu ezitați să urmați, deoarece constructele de cod folosite în acest articol sunt destul de simple și ar trebui să fie ușor de înțeles. Dacă aveți chef, puteți porta, probabil, întregul program într-o limbă la alegerea dvs. destul de ușor.

Înainte de a începe, asigurați-vă că aveți distribuția Go instalată pe sistemul dvs. Pentru a instala Go, puteți urma pașii descriși în documentația oficială.

Apoi, puteți descărca codul sursă al acestui instrument prin clonarea depozitului GitHub. Acest lucru ar trebui să vă fie ușor de urmărit, deoarece fragmentele de cod din acest articol sunt etichetate cu numele de fișiere corespunzătoare. Dacă doriți, îl puteți încerca imediat.

Un avantaj major al utilizării Go pentru acest program este că îl putem construi într-un mod în care avem dependențe externe minime. În cazul nostru, pentru a rula acest program pe un server trebuie doar să ne asigurăm că avem Git și Bash instalate. Deoarece programele Go sunt compilate în binare legate static, puteți compila programul pe computer, îl puteți încărca pe server și îl puteți rula cu aproape zero efort. Pentru majoritatea celorlalte limbi populare de astăzi, acest lucru ar necesita un mediu de execuție uriaș sau un interpret instalat pe server doar pentru a rula automatul de implementare. Programele Go, când sunt făcute corect, pot fi, de asemenea, foarte ușor de mers pe CPU și RAM - ceea ce este ceva ce doriți de la programe ca acesta.

Webhook-uri GitHub

Cu GitHub Webhooks, este posibil să configurați depozitul dvs. GitHub să emită evenimente de fiecare dată când ceva se schimbă în depozit sau un utilizator efectuează anumite acțiuni în depozitul găzduit. Acest lucru permite utilizatorilor să se aboneze la aceste evenimente și să fie notificați prin invocări URL cu privire la diferitele evenimente care au loc în jurul depozitului dvs.

Crearea unui webhook este foarte simplă:

  1. Navigați la pagina de setări a depozitului dvs
  2. Faceți clic pe „Webhooks & Services” din meniul de navigare din stânga
  3. Faceți clic pe butonul „Adăugați webhook”.
  4. Setați o adresă URL și, opțional, un secret (care va permite destinatarului să verifice sarcina utilă)
  5. Faceți alte alegeri pe formular, după cum este necesar
  6. Trimiteți formularul făcând clic pe butonul verde „Adăugați webhook”.

Webhook-uri Github

GitHub oferă o documentație extinsă despre Webhook-uri și cum funcționează exact, ce informații sunt livrate în sarcina utilă ca răspuns la diverse evenimente etc. În scopul acestui articol, ne interesează în special evenimentul „push” care este emis de fiecare dată când cineva împinge către orice ramură a depozitului.

Pachete de construcție

Pachetele de construcție sunt destul de standard în zilele noastre. Folosite de mulți furnizori PaaS, pachetele de compilare vă permit să specificați cum va fi configurată stiva înainte ca o aplicație să fie implementată. Scrierea pachetelor de compilare pentru aplicația dvs. web este foarte ușoară, dar de cele mai multe ori o căutare rapidă pe web vă poate găsi un pachet de compilare pe care îl puteți utiliza pentru aplicația dvs. web fără nicio modificare.

Dacă ați implementat aplicație în PaaS precum Heroku, este posibil să știți deja ce sunt pachetele de compilare și unde să le găsiți. Heroku are o documentație cuprinzătoare despre structura pachetelor de construcție și o listă a unor pachete de construcție populare bine construite.

Programul nostru de automatizare va folosi un script de compilare pentru a pregăti aplicația înainte de a o lansa. De exemplu, un Node.js construit de Heroku analizează fișierul package.json, descarcă o versiune adecvată a Node.js și descarcă dependențe NPM pentru aplicație. Este de remarcat faptul că, pentru a menține lucrurile simple, nu vom avea suport extins pentru pachetele de construcție în programul nostru de prototip. Deocamdată, vom presupune că scripturile buildpack sunt scrise pentru a fi rulate cu Bash și că vor rula pe o instalare Ubuntu nouă așa cum este. Dacă este necesar, puteți extinde cu ușurință acest lucru în viitor pentru a aborda nevoi mai ezoterice.

Profiluri

Profilurile sunt simple fișiere text care vă permit să definiți diferitele tipuri de procese pe care le aveți în aplicația dvs. Pentru majoritatea aplicațiilor simple, ar fi ideal să aveți un singur proces „web”, care ar fi procesul care gestionează cererile HTTP.

Scrierea Procfiles este ușoară. Definiți un tip de proces pe linie tastând numele acestuia, urmat de două puncte, urmat de comanda care va genera procesul:

 <type>: <command>

De exemplu, dacă lucrați cu o aplicație web bazată pe Node.js, pentru a porni serverul web veți executa comanda „node index.js”. Puteți crea pur și simplu un Procfile în directorul de bază al codului și îl denumiți „Procfile” cu următoarele:

 web: node index.js

Vom solicita aplicațiilor să definească tipuri de proces în Procfiles, astfel încât să le putem porni automat după ce introducem codul.

Gestionarea evenimentelor

În programul nostru, trebuie să includem un server HTTP care ne va permite să primim solicitări POST de la GitHub. Va trebui să dedicăm o cale URL pentru a gestiona aceste solicitări de la GitHub. Funcția care va gestiona aceste încărcături utile de intrare va arăta cam așa:

 // hook.go type HookOptions struct { App *App Secret string } func NewHookHandler(o *HookOptions) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { evName := r.Header.Get("X-Github-Event") if evName != "push" { log.Printf("Ignoring '%s' event", evName) return } body, err := ioutil.ReadAll(r.Body) if err != nil { http.Error(w, "Internal Server Error", http.StatusInternalServerError) return } if o.Secret != "" { ok := false for _, sig := range strings.Fields(r.Header.Get("X-Hub-Signature")) { if !strings.HasPrefix(sig, "sha1=") { continue } sig = strings.TrimPrefix(sig, "sha1=") mac := hmac.New(sha1.New, []byte(o.Secret)) mac.Write(body) if sig == hex.EncodeToString(mac.Sum(nil)) { ok = true break } } if !ok { log.Printf("Ignoring '%s' event with incorrect signature", evName) return } } ev := github.PushEvent{} err = json.Unmarshal(body, &ev) if err != nil { log.Printf("Ignoring '%s' event with invalid payload", evName) http.Error(w, "Bad Request", http.StatusBadRequest) return } if ev.Repo.FullName == nil || *ev.Repo.FullName != o.App.Repo { log.Printf("Ignoring '%s' event with incorrect repository name", evName) http.Error(w, "Bad Request", http.StatusBadRequest) return } log.Printf("Handling '%s' event for %s", evName, o.App.Repo) err = o.App.Update() if err != nil { return } }) }

Începem prin a verifica tipul de eveniment care a generat această sarcină utilă. Deoarece ne interesează doar evenimentul „push”, putem ignora toate celelalte evenimente. Chiar dacă configurați webhook-ul să emită doar evenimente „push”, va exista în continuare cel puțin un alt tip de eveniment pe care vă puteți aștepta să îl primiți la punctul final al hook: „ping”. Scopul acestui eveniment este de a determina dacă webhook-ul a fost configurat cu succes pe GitHub.

Apoi, citim întregul corp al cererii primite, calculăm HMAC-SHA1 a acesteia folosind același secret pe care îl vom folosi pentru a configura webhook-ul nostru și determinăm validitatea sarcinii utile de intrare comparând-o cu semnătura inclusă în antetul fișierului. cerere. În programul nostru, ignorăm acest pas de validare dacă secretul nu este configurat. Pe o notă secundară, poate să nu fie o idee înțeleaptă să citim întregul corp fără a avea măcar un fel de limită superioară a câte date vom dori să ne ocupăm aici, dar haideți să păstrăm lucrurile simple pentru a ne concentra asupra aspectelor critice. a acestui instrument.

Apoi folosim o structură din biblioteca client GitHub pentru Go, pentru a elimina încărcătura utilă primită. Deoarece știm că este un eveniment „push”, putem folosi structura PushEvent. Apoi folosim biblioteca standard de codare json pentru a dezmembra încărcătura utilă într-o instanță a structurii. Efectuăm câteva verificări de sănătate și, dacă totul este în regulă, invocăm funcția care începe actualizarea aplicației noastre.

Actualizarea aplicației

Odată ce primim o notificare de eveniment la punctul nostru final de webhook, putem începe să ne actualizăm aplicația. În acest articol, vom arunca o privire la o implementare destul de simplă a acestui mecanism și cu siguranță va fi loc de îmbunătățiri. Cu toate acestea, ar trebui să fie ceva care ne va face să începem cu un proces de implementare automatizat de bază.

diagramă flux a aplicației webhook

Inițializarea depozitului local

Acest proces va începe cu o simplă verificare pentru a determina dacă aceasta este prima dată când încercăm să implementăm aplicația. Vom face acest lucru verificând dacă directorul de depozit local există. Dacă nu există, vom inițializa mai întâi depozitul nostru local:

 // app.go func (a *App) initRepo() error { log.Print("Initializing repository") err := os.MkdirAll(a.repoDir, 0755) // Check err cmd := exec.Command("git", "--git-dir="+a.repoDir, "init") cmd.Stderr = os.Stderr err = cmd.Run() // Check err cmd = exec.Command("git", "--git-dir="+a.repoDir, "remote", "add", "origin", fmt.Sprintf("[email protected]:%s.git", a.Repo)) cmd.Stderr = os.Stderr err = cmd.Run() // Check err return nil }

Această metodă din structura App poate fi folosită pentru a inițializa depozitul local, iar mecanismele sale sunt extrem de simple:

  1. Creați un director pentru depozitul local dacă acesta nu există.
  2. Utilizați comanda „git init” pentru a crea un depozit simplu.
  3. Adăugați o adresă URL pentru depozitul de la distanță în depozitul nostru local și numiți-o „origine”.

Odată ce avem un depozit inițializat, preluarea modificărilor ar trebui să fie simplă.

Preluare modificări

Pentru a prelua modificările din depozitul de la distanță, trebuie doar să invocăm o comandă:

 // app.go func (a *App) fetchChanges() error { log.Print("Fetching changes") cmd := exec.Command("git", "--git-dir="+a.repoDir, "fetch", "-f", "origin", "master:master") cmd.Stderr = os.Stderr return cmd.Run() }

Făcând o „git fetch” pentru depozitul nostru local în acest mod, putem evita problemele cu Git care nu poate avansa rapid în anumite scenarii. Nu că preluările forțate ar fi ceva pe care ar trebui să te bazezi, dar dacă trebuie să faci o împingere forțată în depozitul tău la distanță, aceasta se va descurca cu grație.

Compilarea aplicației

Deoarece folosim scripturi din pachetele de compilare pentru a compila aplicațiile noastre care sunt implementate, sarcina noastră aici este una relativ ușoară:

 // app.go func (a *App) compileApp() error { log.Print("Compiling application") _, err := os.Stat(a.appDir) if !os.IsNotExist(err) { err = os.RemoveAll(a.appDir) // Check err } err = os.MkdirAll(a.appDir, 0755) // Check err cmd := exec.Command("git", "--git-dir="+a.repoDir, "--work-tree="+a.appDir, "checkout", "-f", "master") cmd.Dir = a.appDir cmd.Stderr = os.Stderr err = cmd.Run() // Check err buildpackDir, err := filepath.Abs("buildpack") // Check err cmd = exec.Command("bash", filepath.Join(buildpackDir, "bin", "detect"), a.appDir) cmd.Dir = buildpackDir cmd.Stderr = os.Stderr err = cmd.Run() // Check err cacheDir, err := filepath.Abs("cache") // Check err err = os.MkdirAll(cacheDir, 0755) // Check err cmd = exec.Command("bash", filepath.Join(buildpackDir, "bin", "compile"), a.appDir, cacheDir) cmd.Dir = a.appDir cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr return cmd.Run() }

Începem prin a elimina directorul nostru anterior al aplicației (dacă există). Apoi, creăm unul nou și verificăm conținutul ramurii principale. Apoi folosim scriptul „detectare” din pachetul de compilare configurat pentru a determina dacă aplicația este ceva ce ne putem ocupa. Apoi, creăm un director „cache” pentru procesul de compilare a pachetului de compilare, dacă este necesar. Deoarece acest director persistă între versiuni, se poate întâmpla să nu fie nevoie să creăm un director nou, deoarece unul va exista deja dintr-un proces de compilare anterior. În acest moment, putem invoca scriptul „compilare” din pachetul de compilare și îl putem pregăti să pregătească tot ce este necesar pentru aplicație înainte de lansare. Atunci când pachetele de compilare sunt executate corect, acestea se pot ocupa de stocarea în cache și reutilizarea resurselor stocate anterior în cache pe cont propriu.

Repornirea aplicației

În implementarea noastră a acestui proces de implementare automată, vom opri procesele vechi înainte de a începe procesul de compilare și apoi vom începe procesele noi odată ce faza de compilare este finalizată. Deși acest lucru facilitează implementarea instrumentului, lasă câteva modalități potențial uimitoare de îmbunătățire a procesului de implementare automată. Pentru a îmbunătăți acest prototip, probabil că puteți începe prin a asigura zero timpi de nefuncționare în timpul actualizărilor. Deocamdată, vom continua cu abordarea mai simplă:

 // app.go func (a *App) stopProcs() error { log.Print(".. stopping processes") for _, n := range a.nodes { err := n.Stop() if err != nil { return err } } return nil } func (a *App) startProcs() error { log.Print("Starting processes") err := a.readProcfile() if err != nil { return err } for _, n := range a.nodes { err = n.Start() if err != nil { return err } } return nil }

În prototipul nostru, oprim și începem diferitele procese prin iterarea peste o serie de noduri, unde fiecare nod este un proces corespunzător uneia dintre instanțe ale aplicației (configurat înainte de lansarea acestui instrument pe server). În instrumentul nostru, urmărim starea curentă a procesului pentru fiecare nod. De asemenea, menținem fișiere jurnal individuale pentru ei. Înainte ca toate nodurile să fie pornite, fiecăruia i se atribuie un port unic pornind de la un anumit număr de port:

 // node.go func NewNode(app *App, name string, no int, port int) (*Node, error) { logFile, err := os.OpenFile(filepath.Join(app.logsDir, fmt.Sprintf("%s.%d.txt", name, no)), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) if err != nil { return nil, err } n := &Node{ App: app, Name: name, No: no, Port: port, stateCh: make(chan NextState), logFile: logFile, } go func() { for { next := <-n.stateCh if n.State == next.State { if next.doneCh != nil { close(next.doneCh) } continue } switch next.State { case StateUp: log.Printf("Starting process %s.%d", n.Name, n.No) cmd := exec.Command("bash", "-c", "for f in .profile.d/*; do source $f; done; "+n.Cmd) cmd.Env = append(cmd.Env, fmt.Sprintf("HOME=%s", n.App.appDir)) cmd.Env = append(cmd.Env, fmt.Sprintf("PORT=%d", n.Port)) cmd.Env = append(cmd.Env, n.App.Env...) cmd.Dir = n.App.appDir cmd.Stdout = n.logFile cmd.Stderr = n.logFile err := cmd.Start() if err != nil { log.Printf("Process %s.%d exited", n.Name, n.No) n.State = StateUp } else { n.Process = cmd.Process n.State = StateUp } if next.doneCh != nil { close(next.doneCh) } go func() { err := cmd.Wait() if err != nil { log.Printf("Process %s.%d exited", n.Name, n.No) n.stateCh <- NextState{ State: StateDown, } } }() case StateDown: log.Printf("Stopping process %s.%d", n.Name, n.No) if n.Process != nil { n.Process.Kill() n.Process = nil } n.State = StateDown if next.doneCh != nil { close(next.doneCh) } } } }() return n, nil } func (n *Node) Start() error { n.stateCh <- NextState{ State: StateUp, } return nil } func (n *Node) Stop() error { doneCh := make(chan int) n.stateCh <- NextState{ State: StateDown, doneCh: doneCh, } <-doneCh return nil }

La o privire, acest lucru poate părea puțin mai complicat decât ceea ce am făcut până acum. Pentru a face lucrurile ușor de înțeles, haideți să împărțim codul de mai sus în patru părți. Primele două sunt în cadrul funcției „NewNode”. Când este apelat, populează o instanță a structurii „Nod” și generează o rutină Go care ajută la pornirea și oprirea procesului corespunzător acestui Nod. Celelalte două sunt cele două metode din structura „Node”: „Start” și „Stop”. Un proces este pornit sau oprit prin transmiterea unui „mesaj” printr-un anumit canal pe care această rutină Go per-nod îl urmărește. Puteți transmite fie un mesaj pentru a începe procesul, fie un alt mesaj pentru a-l opri. Deoarece pașii efectivi implicați în pornirea sau oprirea unui proces au loc într-o singură rutină Go, nu există nicio șansă de a obține condiții de cursă.

Rutina Go începe o buclă infinită în care așteaptă un „mesaj” prin canalul „stateCh”. Dacă mesajul transmis către acest canal solicită nodului să înceapă procesul (în interiorul „case StateUp”), acesta folosește Bash pentru a executa comanda. În timp ce face asta, configurează comanda să utilizeze variabilele de mediu definite de utilizator. De asemenea, redirecționează fluxurile standard de ieșire și de eroare către un fișier jurnal predefinit.

Pe de altă parte, pentru a opri un proces (în interiorul „case StateDown”), pur și simplu îl ucide. Aici ați putea, probabil, să fiți creativ și, în loc să omorâți procesul, trimiteți-i imediat un SIGTERM și așteptați câteva secunde înainte de a-l ucide, dând procesului șansa de a se opri cu grație.

Metodele „Start” și „Stop” facilitează transmiterea mesajului corespunzător către canal. Spre deosebire de metoda „Start”, metoda „Stop” așteaptă de fapt ca procesele să fie oprite înainte de a reveni. „Start” transmite pur și simplu un mesaj canalului pentru a începe procesul și revine.

Combinând totul

În cele din urmă, tot ceea ce trebuie să facem este să conectăm totul în cadrul funcției principale a programului. Aici vom încărca și analiza fișierul de configurare, vom actualiza pachetul de compilare, vom încerca să ne actualizăm aplicația o dată și vom porni serverul web pentru a asculta încărcăturile utile de evenimente „push” de pe GitHub:

 // main.go func main() { cfg, err := toml.LoadFile("config.tml") catch(err) url, ok := cfg.Get("buildpack.url").(string) if !ok { log.Fatal("buildpack.url not defined") } err = UpdateBuildpack(url) catch(err) // Read configuration options into variables repo (string), env ([]string) and procs (map[string]int) // ... app, err := NewApp(repo, env, procs) catch(err) err = app.Update() catch(err) secret, _ := cfg.Get("hook.secret").(string) http.Handle("/hook", NewHookHandler(&HookOptions{ App: app, Secret: secret, })) addr, ok := cfg.Get("core.addr").(string) if !ok { log.Fatal("core.addr not defined") } err = http.ListenAndServe(addr, nil) catch(err) }

Deoarece solicităm ca pachetele de compilare să fie simple depozite Git, „UpdateBuildpack” (implementat în buildpack.go) efectuează doar o „clonă git” și un „git pull”, după cum este necesar, cu adresa URL a depozitului pentru a actualiza copia locală a pachetului de compilare.

Încercând

În cazul în care nu ați clonat încă depozitul, o puteți face acum. Dacă aveți instalată distribuția Go, ar trebui să fie posibilă compilarea programului imediat.

 mkdir hopper cd hopper export GOPATH=`pwd` go get github.com/hjr265/toptal-hopper go install github.com/hjr265/toptal-hopper

Această secvență de comenzi va crea un director numit hopper, îl va seta ca GOPATH, va prelua codul din GitHub împreună cu bibliotecile Go necesare și va compila programul într-un binar pe care îl puteți găsi în directorul „$GOPATH/bin”. Înainte de a putea folosi acest lucru pe un server, trebuie să creăm o aplicație web simplă pentru a testa acest lucru. Pentru comoditate, am creat o aplicație web simplă „Bună ziua, lume”, asemănătoare Node.js și am încărcat-o într-un alt depozit GitHub pe care îl puteți bifurca și reutiliza pentru acest test. Apoi, trebuie să încărcăm binarul compilat pe un server și să creăm un fișier de configurare în același director:

 # config.tml [core] addr = ":26590" [buildpack] url = "https://github.com/heroku/heroku-buildpack-nodejs.git" [app] repo = "hjr265/hopper-hello.js" [app.env] GREETING = "Hello" [app.procs] web = 1 [hook] secret = ""

Prima opțiune din fișierul nostru de configurare, „core.addr” este cea care ne permite să configuram portul HTTP al serverului web intern al programului nostru. În exemplul de mai sus, l-am setat la „:26590”, ceea ce va face ca programul să asculte încărcăturile utile de evenimente „push” la „http://{host}:26590/hook”. Când configurați webhook-ul GitHub, înlocuiți „{host}” cu numele de domeniu sau adresa IP care indică serverul dvs. Asigurați-vă că portul este deschis în cazul în care utilizați un fel de firewall.

Apoi, alegem un pachet de compilare setându-i adresa URL Git. Aici folosim pachetul de compilare Node.js al lui Heroku.

Sub „aplicație”, am setat „repo” la numele complet al depozitului tău GitHub care găzduiește codul aplicației. Deoarece găzduiesc aplicația exemplu la „https://github.com/hjr265/hopper-hello.js”, numele complet al depozitului este „hjr265/hopper-hello.js”.

Apoi setăm câteva variabile de mediu pentru aplicație și numărul fiecărui tip de procese de care avem nevoie. Și, în sfârșit, alegem un secret, astfel încât să putem verifica încărcăturile utile de evenimente „push” primite.

Acum putem începe programul nostru de automatizare pe server. Dacă totul este configurat corect (inclusiv implementarea cheilor SSH, astfel încât depozitul să fie accesibil de pe server), programul ar trebui să preia codul, să pregătească mediul folosind pachetul de compilare și să lanseze aplicația. Acum tot ce trebuie să facem este să setăm un webhook în depozitul GitHub pentru a emite evenimente push și să îl îndreptăm către „http://{host}:26590/hook”. Asigurați-vă că înlocuiți „{host}” cu numele de domeniu sau adresa IP care indică serverul dvs.

Pentru a-l testa în sfârșit, faceți câteva modificări în aplicația exemplu și trimiteți-le în GitHub. Veți observa că instrumentul de automatizare va intra imediat în acțiune și va actualiza depozitul de pe server, va compila aplicația și o va reporni.

Concluzie

Din majoritatea experiențelor noastre, putem spune că acesta este ceva destul de util. Aplicația prototip pe care am pregătit-o în acest articol poate să nu fie ceva pe care veți dori să o utilizați pe un sistem de producție așa cum este. Există o mulțime de locuri de îmbunătățire. Un instrument ca acesta ar trebui să aibă o gestionare mai bună a erorilor, să accepte închideri/reporniri grațioase și poate doriți să utilizați ceva precum Docker pentru a conține procesele în loc să le rulați direct. Poate fi mai înțelept să vă dați seama de ce aveți nevoie exact pentru cazul dvs. specific și să veniți cu un program de automatizare pentru asta. Sau poate utilizați o altă soluție, mult mai stabilă, testată în timp, disponibilă pe tot Internetul. Dar în cazul în care doriți să lansați ceva foarte personalizat, sper că acest articol vă va ajuta să faceți asta și să vă arate cât timp și efort ați putea economisi pe termen lung prin automatizarea procesului de implementare a aplicației dvs. web.

Înrudit: Flux Git îmbunătățit explicat