Milijonai priekinių programų valdo su aplinka susijusias versijas. Kiekvienai aplinkai, nesvarbu, ar tai kūrimas, pastatymas ar gamyba, turi būti sukurta atskira sąsajos programos versija ir nustatyti tinkami aplinkos kintamieji. Konstracijų skaičius padaugėja, jei naudojamos kelios programos, o tai padidina nusivylimą. Tai buvo įprasta problema ilgą laiką, tačiau yra geresnis būdas tvarkyti aplinkos kintamuosius. Radau būdą, kaip supaprastinti šį procesą, o šiame straipsnyje žingsnis po žingsnio padėsiu sukurti efektyvų procesą, kuris sumažins kūrimo laiką ir padės užtikrinti projektų aplinkos nuoseklumą.
Prieš pradėdami, manau, turėtume padaryti apibendrinimą. Žiniatinklio programos beveik visada remiasi kintamaisiais, vadinamais „aplinkos kintamaisiais “, kurie dažnai apima vidinius sistemos galutinius taškus, integravimo sistemas, mokėjimo sistemos raktus, išleidimo numerius ir pan. Žinoma, šių kintamųjų reikšmės skiriasi priklausomai nuo aplinkos, kurioje įdiegta programa.
Pavyzdžiui, įsivaizduokite programą, kuri sąveikauja su mokėjimo šliuzu. Kūrimo aplinkoje mokėjimo šliuzo URL gali nukreipti į bandymo smėlio dėžę (https://sandbox.paymentgateway.com), o gamybinėje aplinkoje – į tiesioginę paslaugą (https://live.paymentgateway.com ). Panašiai kiekvienai aplinkai naudojami skirtingi API raktai arba bet koks kitas specifinis aplinkos nustatymas, siekiant užtikrinti duomenų saugumą ir išvengti aplinkų maišymo.
Kuriant pagrindines programas , tai nėra problema. Šiuos kintamuosius pakanka deklaruoti programos kode, nes šių kintamųjų reikšmės saugomos serverio aplinkoje, kurioje yra įdiegta užpakalinė programa. Tokiu būdu užpakalinė programa juos pasiekia paleidžiant.
Tačiau naudojant frontend programas viskas tampa šiek tiek sudėtingesnė. Kadangi jie vykdomi vartotojo naršyklėje, jie neturi prieigos prie konkrečių aplinkos kintamųjų reikšmių. Kad tai būtų išspręsta, šių kintamųjų reikšmės paprastai „įdedamos“ į sąsajos programą kūrimo metu. Tokiu būdu, kai programa veikia vartotojo naršyklėje, visos reikalingos reikšmės jau yra įdėtos į frontend programą.
Šis metodas, kaip ir daugelis kitų, pateikiamas su įspėjimu: kiekvienai aplinkai turite sukurti atskirą tos pačios sąsajos programos versiją, kad kiekvienoje versijoje būtų atitinkamos vertės.
Pavyzdžiui , tarkime, kad turime tris aplinkas:
kūrimas vidiniam testavimui;
integracijos testavimo etapas;
ir gamyba klientams.
Norėdami pateikti savo darbą testavimui, sukurkite programą ir įdiekite ją į kūrimo aplinką. Baigę vidinį testavimą, turite dar kartą sukurti programą, kad galėtumėte ją įdiegti į etapą, o tada dar kartą sukurti, kad galėtumėte įdiegti gamybinėje versijoje. Jei projekte yra daugiau nei viena priekinė programa, tokių versijų skaičius žymiai padidėja. Be to, tarp šių versijų kodų bazė nesikeičia – antroji ir trečioji versijos yra pagrįstos tuo pačiu šaltinio kodu.
Visa tai daro išleidimo procesą sudėtingą, lėtą ir brangų, taip pat kelia kokybės užtikrinimo riziką. Galbūt kūrimas buvo gerai išbandytas kūrimo aplinkoje, tačiau etapo versija yra techniškai nauja, o tai reiškia, kad dabar yra naujų klaidų.
Pavyzdys: turite dvi programas, kurių kūrimo laikas yra X ir Y sekundės. Šiose trijose aplinkose abiejų programų kūrimo laikas užtruks 3X + 3Y . Tačiau jei galėtumėte sukurti kiekvieną programą tik vieną kartą ir naudoti tą pačią versiją visose aplinkose, bendras laikas sutrumpėtų iki X + Y sekundžių, o kūrimo laikas sutrumpėtų tris kartus.
Tai gali turėti didelį skirtumą priekiniuose vamzdynuose, kur ištekliai yra riboti, o kūrimo laikas gali svyruoti nuo kelių minučių iki gerokai daugiau nei valandos. Problema yra beveik kiekvienoje priekinėje programoje visame pasaulyje ir dažnai nėra būdo jos išspręsti. Tačiau tai rimta problema, ypač verslo požiūriu.
Argi nebūtų puiku, jei užuot sukūrę tris atskiras versijas, galėtumėte sukurti vieną ir įdiegti visose aplinkose? Na, aš radau būdą, kaip tai padaryti.
Aplinkos kintamųjų nustatymas
Pirma, savo sąsajos projekto saugykloje turite sukurti failą, kuriame bus pateikti būtini aplinkos kintamieji. Kūrėjas juos naudos vietoje. Paprastai šis failas vadinamas .env.local
, kurį gali nuskaityti dauguma šiuolaikinių sąsajų sistemų. Štai tokio failo pavyzdys:
CLIENT_ID='frontend-development' API_URL=/api/v1' PUBLIC_URL='/' COMMIT_SHA=''
Pastaba: skirtingoms sistemoms reikia skirtingų aplinkos kintamųjų pavadinimų. Pavyzdžiui, „React“ prie kintamųjų pavadinimų turite pridėti REACT_APP_
. Šiame faile nebūtinai turi būti kintamųjų, kurie tiesiogiai veikia programą; jame taip pat gali būti naudingos derinimo informacijos. Pridėjau kintamąjį COMMIT_SHA
, kurį vėliau paimsime iš kūrimo užduoties, kad galėtume stebėti įsipareigojimą, kuriuo buvo pagrįsta ši kūrimo užduotis.
Tada sukurkite failą, pavadintą environment.js
, kuriame galite apibrėžti, kokių aplinkos kintamųjų jums reikia. Frontend sistema juos įves už jus. Pavyzdžiui, „React“ jie saugomi objekte 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, };
Čia gausite visas pradines kintamųjų reikšmes window.ORIGIN_ENVIRONMENTS
objekte, kuris leidžia jas peržiūrėti naršyklės konsolėje. Be to, turite nukopijuoti juos į objektą ENVIRONMENT
, kur taip pat galite nustatyti kai kuriuos numatytuosius nustatymus, pavyzdžiui: manome, kad publicUrl
pagal numatytuosius nustatymus yra /. Naudokite objektą ENVIRONMENT
visur, kur šie kintamieji reikalingi programoje.
Šiame etape įvykdėte visus vietos plėtros poreikius. Tačiau tikslas yra valdyti skirtingas aplinkas.
Norėdami tai padaryti, sukurkite .env
failą su tokiu turiniu:
CLIENT_ID='<client_id>' API_URL='<api_url>' PUBLIC_URL='<public_url>' COMMIT_SHA=$COMMIT_SHA
Šiame faile norėsite nurodyti vietos rezervavimo ženklus kintamiesiems, kurie priklauso nuo aplinkos. Jie gali būti bet kokie, kurių tik norite, jei jie yra unikalūs ir jokiu būdu nesutampa su jūsų šaltinio kodu. Norėdami gauti papildomos garantijos, netgi galite naudoti
Tiems kintamiesiems, kurie nesikeičia įvairiose aplinkose (pvz., įvykdymo maišos), galite tiesiogiai parašyti tikrąsias reikšmes arba naudoti vertes, kurios bus pasiekiamos kūrimo užduoties metu (pvz., $COMMIT_SHA ). Kūrimo proceso metu sąsajos sistema šiuos rezervuotus ženklus pakeis tikrosiomis vertėmis:
Failas
Dabar jūs gaunate galimybę vietoje rezervuotųjų ženklų įdėti tikras vertybes. Norėdami tai padaryti, sukurkite failą inject.py
(aš pasirinkau Python, bet tam galite naudoti bet kokį įrankį), kuriame pirmiausia turėtų būti vietos rezervavimo ženklų susiejimas su kintamųjų pavadinimais:
replacement_map = { "<client_id>": "CLIENT_ID", "<api_url>": "API_URL", "<public_url>": "PUBLIC_URL", "%3Cpublic_url%3E": "PUBLIC_URL" }
Atkreipkite dėmesį, kad public_url
pateikiamas du kartus, o antrame įraše nėra skliaustų. To reikia visiems kintamiesiems, kurie naudojami CSS ir HTML failuose.
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' ]
injector.py
, kuriame gausime kūrimo artefaktų failų (pvz., JS, HTML ir CSS failų) susiejimą ir sąrašą ir pakeisime vietos rezervavimo ženklus kintamųjų reikšmėmis iš dabartinės aplinkos:
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)
Tada faile inject.py
pridėkite šią eilutę (nepamirškite importuoti injector.py
):
injector.inject(target_files, replacement_map, base_path)
inject.py
scenarijus veikia tik diegimo metu. Įdiegę Python ir nukopijavę visus artefaktus, galite pridėti jį prie Dockerfile
komandoje CMD
: 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.
tai viskas! Tokiu būdu kiekvieno diegimo metu bus naudojami iš anksto sukurti failai, į kuriuos įterpiami diegimo aplinkai būdingi kintamieji.
Failas:
Vienas dalykas – jei jūsų kūrimo artefaktų failų pavadinimuose yra turinio maišos, ši injekcija neturės įtakos failų pavadinimams ir dėl to gali kilti problemų dėl naršyklės talpyklos saugojimo. Norėdami tai išspręsti, pakeitę failus su įterptais kintamaisiais, turėsite:
Norėdami tai įgyvendinti, į failą inject.py
pridėkite maišos bibliotekos importą ( import hashlib
) ir šias funkcijas.
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
Tačiau ne visus failus reikia pervardyti. Pavyzdžiui, index.html
failo pavadinimas turi likti nepakitęs ir kad tai pasiektumėte, sukurkite TargetFile
klasę, kurioje bus saugoma vėliavėlė, nurodanti, ar reikia pervadinti:
class TargetFile: def __init__(self, glob, should_be_renamed = True): self.glob = glob self.should_be_renamed = should_be_renamed
Dabar tereikia pakeisti failų kelių masyvą inject.py
į TargetFile
klasės objektų masyvą:
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) ]
Ir atnaujinkite inject
funkciją injector.py
kad įtrauktumėte failo pervadinimą, jei vėliavėlė nustatyta:
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)
Todėl artefaktų failai bus pavadinti tokiu formatu: <origin-file-name>
. <injection-hash>
. <extension>
.
Failo pavadinimas prieš injekciją:
Failo pavadinimas po injekcijos:
Tie patys aplinkos kintamieji duoda tą patį failo pavadinimą, leidžiantį vartotojo naršyklei tinkamai išsaugoti failą talpykloje. Dabar yra garantija, kad teisingos šių kintamųjų reikšmės bus saugomos naršyklės talpykloje, todėl klientui bus geresnis našumas.
Dėl tradicinio požiūrio, kad kiekvienai aplinkai būtų kuriamos atskiros versijos, atsirado keletas kritinių neveiksmingumo, o tai gali būti problema komandoms, turinčioms ribotus išteklius.
Dabar turite leidimo proceso planą, kuris gali išspręsti užsitęsusį diegimo laiką, perteklinį kūrimą ir padidėjusią frontend programų kokybės užtikrinimo riziką – visa tai. Visa tai kartu įvedant naują garantuoto nuoseklumo lygį visose aplinkose.
Užuot prireikę N versijų, jums reikės tik vieno. Būsimam leidimui galite tiesiog įdiegti jau išbandytą versiją, kuri taip pat padeda išspręsti galimas trikčių problemas, nes ta pati versija bus naudojama visose aplinkose. Be to, šio scenarijaus vykdymo greitis yra nepalyginamai greitesnis nei net labiausiai optimizuoto kūrimo. Pavyzdžiui, vietiniai etalonai „MacBook 14 PRO“, M1, 32 GB yra tokie:
Mano požiūris supaprastina išleidimo procesą, palaiko programos našumą, leisdamas veiksmingas talpyklos strategijas ir užtikrina, kad su kūrimu susijusios klaidos nepateks į aplinką. Be to, visas laikas ir pastangos, kurios anksčiau buvo skiriamos varginančioms kūrimo užduotims, dabar gali būti sutelktos į dar geresnės vartotojo patirties kūrimą. Ko nemylėti?
Užtikriname, kad su kūrimu susijusios klaidos nepatektų į programą, skirtą kitoms aplinkoms. Gali būti fantominių klaidų, kurios atsiranda dėl kūrimo sistemų trūkumų. Tikimybė nedidelė, bet jos egzistuoja.