Nx affected-пен нөлден CI: «пайплайн жоқтан» барлық staging-қолданбаларға 9 минутқа дейін
Осы бетте
nx affected. Қиындық командада емес, «неге қатысты қозғалған?» деген сұрақта. Төменде — скриптермен қолмен деплой жасаудан MR-ды тексеретін және тек керегін құрастыратын пайплайнға дейінгі жол, және жолда басқан төрт тырма.Нөлдік нүкте: қолмен деплой
Алғашында пайплайн болған жоқ. Shell-скрипт болды: қолданбаны жергілікті құрастырып, файлдарды серверге көшіру. Мәселелер айқын:
- репозиторийдегі емес, сенің дискіңде жатқан нәрсе құрастырылады;
- деплойға дейін ешқандай тексеріс жоқ;
- деплойды тек қолжетімділігі мен скрипті бар адам ғана жасай алады.
Алғашқы пайплайн скриптті қайталады: тәуелділіктерді орнату → барлық қолданбаларды құрастыру → көшіру. Жақсырақ, бірақ әр пушқа барлық қолданбалар құрастырылатын.
Affected идеясы, қарапайым тілмен
Nx жобаның графын біледі: қай қолданба қай кітапханаларға тәуелді. Егер users кітапханасындағы файлды өзгертсең, Nx граф бойымен «жоғары» жүріп, осы кітапхананы қолданатын барлық қолданбаларды табады. Қалғандары қозғалмаған, оларды құрастырмауға және тестілемеуге болады.
npx nx affected -t build --base=<коммит> --head=HEADNx base мен head арасындағы өзгерген файлдарды алады (шын мәнінде, git diff), олардың жобаларын табады, оларға тәуелдінің бәрін қосады және build-ні тек осы жиынға іске қосады.
Бұл қарапайым графты аралау. Кітапханалар — төбелер, ал импорттар — қабырғалар болса, «қозғалғандар» — қабырғалар бойымен өзгергенге жетуге болатын барлық төбелер.
Басты сұрақ: base дегеніміз не?
Команда қарапайым, ал base-мен қателесу — оңай. Әртүрлі міндеттерге жауап әртүрлі.
MR тексеру үшін: тармақталу нүктесі
Merge request үшін база — тармақ мақсатты тармақтан бөлінген коммит (merge-base). GitLab-та бұл CI_MERGE_REQUEST_DIFF_BASE_SHA айнымалысы.
Маңызды бөлшек: әдепкі бойынша GitLab репозиторийді тарихтың шектеулі тереңдігімен клондайды. Толық тарихсыз git merge-base-ті таба алмайды, ал affected дұрыс санамайды. Сондықтан:
variables:
GIT_DEPTH: "0" # полная историяStaging құрастыру үшін: соңғы деплой жасалған коммит
Бірінші ой — тармақтың алдыңғы коммитімен (CI_COMMIT_BEFORE_SHA) салыстыру. Бұл қате, себебі мынада.
Бізде staging пайплайндары үзілетін (interruptible: true): жаңа пуш келсе, ескі құрастыру тоқтатылады — оны бәрібір жаңасы басып кетеді. Елестетіп көр:
- A коммиті X қолданбасын өзгертеді. A пайплайны басталады.
- Бір минуттан кейін B коммиті Y қолданбасын өзгертеді. A пайплайны тоқтатылды, B басталады.
- B өзін A-мен салыстырып, тек Y-ты құрастырады.
X өзгерістері бірде-бір құрастыруға кірмеді. Серверде X-тің ескі нұсқасы тұр, ал неге екенін ешкім білмейді.
Дұрыс база — серверге шынымен жеткен соңғы коммит. Оны сәтті деплойдан кейін бөлек job есте сақтайды:
# учебный пример
mark-deployed:
stage: mark_deployed
needs: [deploy]
cache:
key: deployed-sha
paths: [.ci-cache/]
policy: push
script:
- mkdir -p .ci-cache
- echo "$CI_COMMIT_SHA" > .ci-cache/deployed-shaҚұрастыру осы файлды оқып, affected-ті содан бастап санайды. Ол тек сәтті деплойдан кейін ғана жазылады, сондықтан құлаған және тоқтатылған пайплайндардың өзгерістері келесі құрастыруға автоматты түрде кіреді.
Және міндетті түрде — қосалқы шығу жолы: бәрін құрастыру, егер:
- база жоқ болса (алғашқы іске қосу, кэш тазаланған);
- тарихта мұндай коммит жоқ болса (мысалы, force-push-тан кейін);
- CI-дың өзі немесе қолданбалар тізімі өзгерсе;
- біреу пайплайнды
FULL_BUILD=true-мен іске қосса.
Бір рет керегін құрастырмағаннан гөрі, кейде артығын құрастырған жақсы.
MR тексеру: тек өзгергенін
Құрастыру — жартысы ғана. Екінші жартысы — қателерді staging-ке жібермеу. Әр MR-ға verify кезеңі, бәрі affected бойынша:
| Job | Не істейді |
|---|---|
| lint | тек өзгерген файлдар бойынша ESLint + стильдерді тексеру |
| typecheck | шығарусыз Angular компиляторы (ngc --noEmit): типтер мен шаблондар |
| test | қозғалған жобалардың unit-тесттері, MR виджетіне JUnit есебі мен қамту |
Үш job қатар жүреді. Линтердің ескі бұзушылықтары базлайн-файлға жазылған және кедергі келтірмейді; тек жаңасы құлатады.
Дәл осыны жергілікті бір командамен де (pnpm verify). Тексеріс тек CI-да болса, адам қате туралы он минуттан кейін біледі. Жергілікті де болса — пушқа дейін.
1-тырма. Қатар екі құрастыру — OOM
Жылдамдатқым келді: бір уақытта екі қолданбадан құрастыру (--parallel=2). Құрастырулар esbuild-тің оғаш қатесімен құлай бастады: all goroutines are asleep - deadlock!.
Қате адастырады. Шын себебі — жад. Angular-дың қатар екі құрастыруы 15,5 ГБ хостта шамамен 13 ГБ алатын. Linux ядросы (OOM killer) құрастыру процесін өлтіретін, ал ата-анасыз қалған esbuild еншілес процесі осы хабарламамен құлайтын.
Сабақ: CI-да оғаш қате болса, алдымен ресурстарды қара — жад, диск, уақыт. Шешімі — --parallel=1. Бір құрастыруға баяуырақ, бірақ affected олардың санын онсыз да азайтты.
2-тырма. Тесттерді жасырған кэш
Nx нәтижелерді кэштей алады: тапсырманың кірістері өзгермесе, ол оны іске қоспай, нәтижені кэштен (соның ішінде қашықтағы Nx Cloud-тан) қалпына келтіреді.
MR есебінде кенет 944 тесттің 237-сі ғана қалды. Тесттерді ешкім өшірмеген. Себебі: кэштен тапсырманың тек жарияланған шығыстары (outputs) қалпына келтіріледі. Есепке арналған junit.xml файлы оларға кірмеген. Кэштен келген жоба — «жасыл», бірақ есепсіз, ал GitLab оны жай көрсетпейді.
MR-дағы тесттер үшін шешім — --skip-nx-cache. Есеп үшін адал жүргізу керек. Баламасы — тапсырманың outputs ішіне junit.xml қосу.
Сабақ: кэш жылдамдатады, бірақ кейін файлдардан оқитын нәрсенің бәрі outputs ішінде болуы керек. Әйтпесе ол үнсіз жоғалады.
3-тырма. Ешкім қолданбайтынды құрастыру
Staging-тегі 17 қолданбаның 7-еуі ғана шынымен тестіленетін. Қалғандары тізімде болғандықтан ғана құрастырылатын. Деплой картасында артықтарын өшірдік — бір жол ақылды кодсыз жұмыстың жартысынан көбі азайды.
Ең жылдам құрастыру — жоқ құрастыру.
4-тырма. CI-ды веб-редактор арқылы түзеу
Пайплайнның алғашқы нұсқасын тікелей GitLab веб-редакторында түзедім: бір аптада тарихта «Update .gitlab-ci.yml file» деген жиырма бестей коммит қалды. Әрқайсысы — іске қос, күт, қара, түзе.
Басқаша не істеу керек:
- CI-ды тармақта жазып, MR-да тексеру;
- YAML-ды пушқа дейін GitLab-тың кірістірілген линтерімен (CI Lint) тексеру;
- логиканы YAML-дан жергілікті іске қосып, жөндеуге болатын скрипттерге (
node tools/scripts/...) шығару.
Минуттарды үнемдейтін ұсақ-түйек
pnpm-lock.yamlкілті бойынша pnpm сторының кэші: тәуелділіктер әр жолы қайтадан жүктелмейді.- Бір job кэшті жазады, қалғандары тек оқиды (
policy: pull): әйтпесе қатар үш job бір нәрсені кезекпен архивтеп, жүктейді. - Source maps қателерді бақылау жүйесіне құрастырумен бір job-та жүктеледі және деплойдың алдында жойылады: серверге олар керек емес, артефакт кішірейеді.
Қорытынды
| Бұрын | Қазір |
|---|---|
| скриптпен қолмен деплой | әр пушқа пайплайн |
| тексерістер жоқ | әр MR-ға lint + typecheck + тесттер |
| барлық қолданбаларды құрастыру | тек соңғы деплойдан бері қозғалғандары |
| ? минут | барлық staging-қолданбаларға ~9 минут |
Түйгенім: nx affected — командаға емес, «неге қатысты?» деген сұраққа дұрыс жауапқа қатысты. Және жауап жоқ кезде «бәрін құрастыр» деген қосалқы шығу жолына.
Байланысты жазбалар: nx-monorepo-from-copies, tailwind-v3-to-v4-monorepo, custom-eslint-rules.