paint-brush
Rad s varijablama okruženja u izdanjima Frontend aplikacijaby@egorgrushin

Rad s varijablama okruženja u izdanjima Frontend aplikacija

by Egor Grushin11m2024/10/30
Read on Terminal Reader

Predugo; Citati

Frontend aplikacije tradicionalno zahtijevaju zasebne gradnje za svako okruženje (razvoj, faza, proizvodnja) jer su varijable okruženja ugrađene u vrijeme izrade, što dovodi do produženog vremena izdavanja. Predlažem rješenje za jednokratnu izgradnju frontend aplikacije i ubacivanje varijabli specifičnih za okruženje tokom implementacije korištenjem rezerviranih mjesta i skripte da ih zamijeni stvarnim vrijednostima u vrijeme implementacije. Ova metoda pojednostavljuje proces izdavanja, skraćuje vrijeme izrade, osigurava konzistentnost u svim okruženjima i minimizira rizik od grešaka u vezi sa izgradnjom. Dodatno, upravljanjem heširanjem imena fajla nakon ubrizgavanja promenljive, pristup održava efikasno keširanje pretraživača i ukupne performanse aplikacije.
featured image - Rad s varijablama okruženja u izdanjima Frontend aplikacija
Egor Grushin HackerNoon profile picture
0-item


Milioni front-end aplikacija upravljaju izgradnjom specifičnim za okruženje. Za svako okruženje — bilo da se radi o razvoju, scenskom ili produkcijskom — mora se kreirati zasebna verzija prednje aplikacije i postaviti prave varijable okruženja. Broj izrada se umnožava ako je uključeno više aplikacija, što povećava frustraciju. Ovo je uobičajen problem već duže vrijeme, ali postoji bolji način za rukovanje varijablama okruženja. Pronašao sam način da pojednostavim ovaj proces i u ovom članku ću vas voditi korak po korak kako biste kreirali efikasan proces koji će smanjiti vrijeme izrade i pomoći vam da osigurate dosljednost u svim okruženjima u vašim projektima.


Razumijevanje varijabli okruženja

Prije nego što počnemo, mislim da bi trebali napraviti rezime. Web aplikacije se gotovo uvijek oslanjaju na varijable poznate kao "varijable okruženja ", koje često uključuju interne krajnje tačke sistema, sisteme integracije, ključeve platnog sistema, brojeve izdanja i tako dalje. Naravno, vrijednosti ovih varijabli razlikuju se ovisno o okruženju u kojem je aplikacija raspoređena.


Na primjer, zamislite aplikaciju koja je u interakciji s pristupom za plaćanje. U razvojnom okruženju, URL pristupnika plaćanja može ukazivati na sandbox za testiranje (https://sandbox.paymentgateway.com), dok u proizvodnom okruženju upućuje na uslugu uživo (https://live.paymentgateway.com ). Slično tome, različiti API ključevi ili bilo koje drugo specifično okruženje se koriste za svako okruženje kako bi se osigurala sigurnost podataka i izbjeglo miješanje okruženja.


Izazovi u razvoju frontenda

Prilikom izrade pozadinskih aplikacija , to nije problem. Deklarisanje ovih varijabli u kodu aplikacije je dovoljno jer se vrijednosti ovih varijabli pohranjuju u serverskom okruženju gdje je pozadinski dio raspoređen. Na ovaj način, pozadinska aplikacija im pristupa pri pokretanju.


Međutim, s frontend aplikacijama stvari postaju nešto složenije. Pošto se pokreću u pretraživaču korisnika, nemaju pristup određenim vrijednostima varijabli okruženja. Da bi se ovo pozabavilo, vrijednosti ovih varijabli se obično "peče" u frontend aplikaciju u vrijeme izrade. Na ovaj način, kada se aplikacija pokrene u pretraživaču korisnika, sve potrebne vrijednosti su već ugrađene u frontend aplikaciju.


Ovaj pristup, kao i mnogi drugi, dolazi sa upozorenjem: potrebno je da kreirate zasebnu verziju iste frontend aplikacije za svako okruženje tako da svaka verzija sadrži svoje vrednosti.


Na primjer , recimo da imamo tri okruženja:

  • razvoj za interno testiranje;

  • faza za testiranje integracije;

  • i proizvodnja za kupce.


Da biste svoj rad poslali na testiranje, izgradite aplikaciju i implementirate je u razvojno okruženje. Nakon što je interno testiranje završeno, morate ponovo izgraditi aplikaciju da biste je postavili u fazu, a zatim je ponovo izgraditi za implementaciju u proizvodnju. Ako projekat sadrži više od jedne front-end aplikacije, broj takvih nadgradnja se značajno povećava. Osim toga, između ovih build-ova, kodna baza se ne mijenja — druga i treća verzija su bazirane na istom izvornom kodu.


Sve ovo čini proces izdavanja glomaznim, sporim i skupim, kao i rizik za osiguranje kvaliteta. Možda je build dobro testiran u razvojnom okruženju, ali scenski build je tehnički nov, što znači da sada postoji novi potencijal za grešku.


Primjer: imate dvije aplikacije s vremenom izrade od X i Y sekundi. Za ta tri okruženja, obje aplikacije bi trebale 3X + 3Y u vremenu izrade. Međutim, kada biste svaku aplikaciju mogli napraviti samo jednom i koristiti istu verziju u svim okruženjima, ukupno vrijeme bi se smanjilo na samo X + Y sekundi, trostruko skratilo vrijeme izrade.

Ovo može napraviti veliku razliku u frontend kanalima, gdje su resursi ograničeni, a vrijeme izrade može varirati od samo nekoliko minuta do više od jednog sata. Problem je prisutan u gotovo svakoj frontend aplikaciji na globalnom nivou i često ne postoji način da se riješi. Ipak, ovo je ozbiljan problem, posebno iz poslovne perspektive.

Zar ne bi bilo sjajno kada biste umjesto stvaranja tri odvojene verzije mogli samo napraviti jednu i primijeniti je u svim okruženjima? Pa, našao sam način da uradim upravo to.


Optimiziranje frontend implementacije. Priručnik

Postavljanje varijabli okruženja


  1. Prvo, trebate kreirati datoteku u spremištu vašeg frontend projekta gdje će biti navedene potrebne varijable okruženja. Ovo će koristiti programer lokalno. Tipično, ova datoteka se zove .env.local , koju većina modernih frontend okvira može čitati. Evo primjera takve datoteke:


     CLIENT_ID='frontend-development' API_URL=/api/v1' PUBLIC_URL='/' COMMIT_SHA=''


    Napomena: različiti okviri zahtijevaju različite konvencije imenovanja za varijable okruženja. Na primjer, u Reactu, trebate dodati REACT_APP_ imenima varijabli. Ova datoteka ne mora nužno uključivati varijable koje direktno utječu na aplikaciju; također može sadržavati korisne informacije za otklanjanje grešaka. Dodao sam varijablu COMMIT_SHA , koju ćemo kasnije povući iz zadatka izgradnje da bismo pratili urezivanje na kojem se zasniva ova gradnja.


  2. Zatim kreirajte datoteku pod nazivom environment.js , gdje možete definirati koje varijable okruženja su vam potrebne. Frontend framework će ih ubaciti za vas. Za React, na primjer, oni su pohranjeni u objektu process.env :


     const ORIGIN_ENVIRONMENTS = window.ORIGIN_ENVIRONMENTS = { CLIENT_ID: process.env.CLIENT_ID, API_URL: process.env.API_URL, PUBLIC_URL: process.env.PUBLIC_URL, COMMIT_SHA: process.env.COMMIT_SHA }; export const ENVIRONMENT = { clientId: ORIGIN_ENVIRONMENTS.CLIENT_ID, apiUrl: ORIGIN_ENVIRONMENTS.API_URL, publicUrl: ORIGIN_ENVIRONMENTS.PUBLIC_URL ?? "/", commitSha: ORIGIN_ENVIRONMENTS.COMMIT_SHA, };


  1. Ovdje preuzimate sve početne vrijednosti za varijable u objektu window.ORIGIN_ENVIRONMENTS , koji vam omogućava da ih vidite u konzoli pretraživača. Plus, morate ih kopirati u objekt ENVIRONMENT , gdje također možete postaviti neke zadane vrijednosti, na primjer: pretpostavljamo da je publicUrl / po defaultu. Koristite objekt ENVIRONMENT gdje god su ove varijable potrebne u aplikaciji.


    U ovoj fazi ispunili ste sve potrebe lokalnog razvoja. Ali cilj je nositi se s različitim okruženjima.


  2. Da biste to učinili, kreirajte .env datoteku sa sljedećim sadržajem:

 CLIENT_ID='<client_id>' API_URL='<api_url>' PUBLIC_URL='<public_url>' COMMIT_SHA=$COMMIT_SHA

U ovoj datoteci želite da navedete čuvare mesta za varijable koje zavise od okruženja. Oni mogu biti sve što želite, sve dok su jedinstveni i ni na koji način se ne preklapaju s vašim izvornim kodom. Za dodatnu sigurnost, možete čak koristiti UUID-ovi – Univerzalno jedinstveni identifikatori.


Za one varijable koje se ne mijenjaju u različitim okruženjima (npr. heš urezivanja), možete direktno napisati stvarne vrijednosti ili koristiti vrijednosti koje će biti dostupne tokom zadatka izgradnje (kao što je $COMMIT_SHA ). Frontend okvir će zamijeniti ove čuvare mjesta stvarnim vrijednostima tokom procesa izgradnje:


File
File

  1. Sada imate priliku da unesete stvarne vrijednosti umjesto rezerviranih mjesta. Da biste to uradili, kreirajte datoteku, inject.py (ja sam izabrao Python, ali možete koristiti bilo koji alat za ovu svrhu), koja prvo treba da sadrži mapiranje čuvara mesta u nazive varijabli:


 replacement_map = { "<client_id>": "CLIENT_ID", "<api_url>": "API_URL", "<public_url>": "PUBLIC_URL", "%3Cpublic_url%3E": "PUBLIC_URL" }

Imajte na umu da je public_url naveden dva puta, a drugi unos ima izvučene zagrade. Ovo vam je potrebno za sve varijable koje se koriste u CSS i HTML datotekama.


  1. Sada dodajmo listu datoteka koje želimo izmijeniti (ovo bi bio primjer za Nginx):


 base_path = 'usr/share/nginx/html' target_files = [ f'{base_path}/static/js/main.*.js', f'{base_path}/static/js/chunk.*.js', f'{base_path}/static/css/main.*.css', f'{base_path}/static/css/chunk.*.css', f'{base_path}/index.html' ]


  1. Zatim kreiramo datoteku injector.py , gdje ćemo primiti mapiranje i listu datoteka artefakata građenja (kao što su JS, HTML i CSS datoteke) i zamijeniti čuvare mjesta vrijednostima varijabli iz našeg trenutnog okruženja:


 import os import glob def inject_envs(filename, replacement_map): with open(filename) as r: lines = r.read() for key, value in replacement_map.items(): lines = lines.replace(key, os.environ.get(value) or '') with open(filename, "w") as w: w.write(lines) def inject(target_files, replacement_map, base_path): for target_file in target_files: for filename in glob.glob(target_file.glob): inject_envs(filename, replacement_map)


A zatim, u datoteku inject.py , dodajte ovaj red (ne zaboravite uvesti injector.py ):

 injector.inject(target_files, replacement_map, base_path)


  1. Sada moramo biti sigurni da se inject.py skripta pokreće samo tokom implementacije. Možete je dodati u Dockerfile u CMD naredbi nakon instalacije Pythona i kopiranja svih artefakata:
 RUN apk add python3 COPY nginx/default.conf /etc/nginx/conf.d/default.conf COPY --from=build /app/ci /ci COPY --from=build /app/build /usr/share/nginx/html CMD ["/bin/sh", "-c", "python3 ./ci/inject.py && nginx -g 'daemon off;'"]That's it! This way, during each deployment, the pre-built files will be used, with variables specific to the deployment environment injected into them.


To je to! Na ovaj način, tokom svake implementacije, koristiće se unapred izgrađeni fajlovi, sa varijablama specifičnim za okruženje implementacije koje će biti ubačene u njih.


Fajl: File


Rukovanje heširanjem imena fajla za pravilno keširanje pretraživača

Jedna stvar – ako vaši artefakti izrade uključuju heš sadržaja u svoje nazive datoteka, ova injekcija neće utjecati na nazive datoteka i to bi moglo uzrokovati probleme s keširanjem pretraživača. Da biste to popravili, nakon izmjene datoteka s ubačenim varijablama, morat ćete:


  1. Generirajte novi hash za ažurirane datoteke.
  2. Dodajte ovaj novi hash imenima datoteka, tako da ih pretraživač tretira kao nove datoteke.
  3. Ažurirajte sve reference na stare nazive datoteka u vašem kodu (kao što su izjave o uvozu) kako bi odgovarale novim imenima datoteka.


Da biste to implementirali, dodajte uvoz hash biblioteke ( import hashlib ) i sljedeće funkcije u datoteku inject.py .


 def sha256sum(filename): h = hashlib.sha256() b = bytearray(128 * 1024) mv = memoryview(b) with open(filename, 'rb', buffering=0) as f: while n := f.readinto(mv): h.update(mv[:n]) return h.hexdigest() def replace_filename_imports(filename, new_filename, base_path): allowed_extensions = ('.html', '.js', '.css') for path, dirc, files in os.walk(base_path): for name in files: current_filename = os.path.join(path, name) if current_filename.endswith(allowed_extensions): with open(current_filename) as f: s = f.read() s = s.replace(filename, new_filename) with open(current_filename, "w") as f: f.write(s) def rename_file(fullfilename): dirname = os.path.dirname(fullfilename) filename, ext = os.path.splitext(os.path.basename(fullfilename)) digest = sha256sum(fullfilename) new_filename = f'{filename}.{digest[:8]}' new_fullfilename = f'{dirname}/{new_filename}{ext}' os.rename(fullfilename, new_fullfilename) return filename, new_filename


Ipak, ne treba preimenovati sve datoteke. Na primjer, ime datoteke index.html mora ostati nepromijenjeno i da biste to postigli, kreirajte klasu TargetFile koja će pohraniti oznaku koja pokazuje da li je preimenovanje potrebno:


 class TargetFile: def __init__(self, glob, should_be_renamed = True): self.glob = glob self.should_be_renamed = should_be_renamed


Sada samo morate zamijeniti niz putanja datoteke u inject.py nizom objekata klase TargetFile :

 target_files = [ injector.TargetFile(f'{base_path}/static/js/main.*.js'), injector.TargetFile(f'{base_path}/static/js/chunk.*.js'), injector.TargetFile(f'{base_path}/static/css/main.*.css'), injector.TargetFile(f'{base_path}/static/css/chunk.*.css'), injector.TargetFile(f'{base_path}/index.html', False) ]


I ažurirajte funkciju inject u injector.py da uključi preimenovanje datoteke ako je postavljena zastavica:


 def inject(target_files, replacement_map, base_path): for target_file in target_files: for filename in glob.glob(target_file.glob): inject_envs(filename, replacement_map) if target_file.should_be_renamed: filename, new_filename = rename_file(filename) replace_filename_imports(filename, new_filename, base_path)


Kao rezultat, datoteke artefakata će slijediti ovaj format imenovanja: <origin-file-name> . <injection-hash> . <extension> .


Naziv datoteke prije ubrizgavanja:

Ime datoteke prije ubrizgavanja


Naziv datoteke nakon ubrizgavanja:Ime datoteke nakon ubrizgavanja


Iste varijable okruženja daju isto ime datoteke, omogućavajući korisnikovom pretraživaču da ispravno kešira datoteku. Sada postoji garancija da će ispravne vrijednosti ovih varijabli biti pohranjene u keš pretraživača, kao rezultat – bolje performanse za klijenta.


Rješenje za pojednostavljenu implementaciju

Tradicionalni pristup kreiranja zasebnih verzija za svako okruženje doveo je do nekoliko kritičnih neefikasnosti, što može biti problem za timove sa ograničenim resursima.


Sada imate nacrt za proces izdavanja koji može riješiti produžena vremena implementacije, prekomjerne gradnje i povećane rizike u osiguranju kvaliteta za frontend aplikacije – sve to. Sve to uz uvođenje novog nivoa zagarantovane konzistentnosti u svim okruženjima.


Umjesto da treba N build, trebat će vam samo jedna. Za nadolazeće izdanje, možete jednostavno implementirati verziju koja je već testirana, što također pomaže u rješavanju potencijalnih problema s greškom jer će se ista verzija koristiti u svim okruženjima. Osim toga, brzina izvršavanja ove skripte je neuporedivo brža od čak i najoptimizovanije verzije. Na primjer, lokalni benchmarkovi na MacBook 14 PRO, M1, 32GB su sljedeći:


Moj pristup pojednostavljuje proces izdavanja, održava performanse aplikacije omogućavajući efikasne strategije keširanja i osigurava da greške vezane za izgradnju neće dospjeti u okruženja. Osim toga, svo vrijeme i trud koji su prethodno utrošeni na dosadne zadatke izgradnje sada se mogu usmjeriti na stvaranje još boljeg korisničkog iskustva. Šta ne treba voljeti?


Osiguravamo da greške vezane za izgradnju ne uđu u aplikaciju za druga okruženja. Mogu postojati fantomske greške koje se pojavljuju zbog nesavršenosti u sistemima izgradnje. Šanse su male, ali postoje.



L O A D I N G
. . . comments & more!

About Author

Egor Grushin HackerNoon profile picture
Egor Grushin@egorgrushin
11+ years of experience both in software development and architecture for highload B2B and B2C projects.

HANG TAGS

OVAJ ČLANAK JE PREDSTAVLJEN U...