Azure – Deploy Node.js + Vue + Webpack

Nie mogłem doczekać się aż napiszę tego posta. Minęło kilka dni, cisza nastała na blogu ale była spowodowana spadkiem mocy i frustracją spowodowaną wieloma bezowocnymi próbami zbudowania aplikacji node.js na serwerze Azure. Ale w końcu udało się. Można powiedzieć, że mam już prawie pełne CI 😏

Scenariusz wdrożenia

Zacznę od opisu jaki efekt chciałem uzyskać.
Azure deploy – node.js, vue.js, webpack
Lokalne zmiany są zatwierdzane i „wypychane” na serwer githuba, Następnie Azure poprzez skonfigurowany automatyczny deploy rozpoczyna całą procedurę wdrożenia:
  • Pobiera kod źródłowy z githuba do folderu homesiterepository
  • Kopiuje pliki do folderu homesitewwwroot
  • Wykonuje instalację node_modules
Tak to wygląda bardzo poglądowo, w tle dzieje się wiele więcej rzeczy. 
W tym całym procesie brakowało mi aby to Azure był odpowiedzialny za uruchomienie webpacka i wykonanie całej tej procedury transformacji plików .js, .vue itd. tak aby pozbyć się z repozytorium „/dist”.

Potencjalne problemy

Jak wspomniałem na początku posta, droga nie była łatwa ale też w dużej mierze przez brak doświadczenia jak działa deploy Azure i node.js w środowisku produkcyjnym. Oto kilka wskazówek:
  • Node.js na Azure działa w trybie „production”. Weź to pod uwagę gdy chcesz uruchamiać skrypty które coś budują. Tutaj nie masz skonfigurowanego środowiska deweloperskiego adhoc.
  • Zwróć uwagę na użytą wersję node.js oraz npm. Większość moich problemów wynikała z dość starej wersji npm 1.4. Można to zmienić dodając ustawienie WEBSITE_NODE_DEFAULT_VERSION. Listą dostępnych runtime jest duża i można ją zobaczyć pod adresem: https://your_site_name.scm.azurewebsites.net/api/diagnostics/runtime
  • Zanim doszedłem do zmiany wersji node.js przez długi czas borykałem się ze znanym z wcześniejszych wersji windows problemem maksymalnego rozmiaru nazwy ścieżki ograniczonego do 260 znaków. Jest dostępne obejście tego problemu: #max_path-explanation-and-workarounds
  • Pamiętaj aby nie kopiować nie potrzebnych rzeczy do folderu docelowego, np. logi z builda, node_modules typu devDependencies

#1 Własny skrypt deploy.cmd

Aby ruszyć z tematem wpierw należy wykonać modyfikację automatycznego deploy poprzez utworzenie własnego skryptu. Służy do tego narzędzie azure-cli https://www.npmjs.com/package/azure-cli. Tworzy ona dwa pliki .deployment oraz deploy.cmd. Obydwa należy umieścić w katalogu root aplikacji. Do ich utworzenia służy komenda:
azure site deploymentscript –node
deploy.cmd jest skryptem batch, w którym widać kilka komend ustawiających zmienne do poleceń jak i ich wywołania. Głównie są to operacje kopiowania plików pomiędzy folderem %DEPLOYMENT_SOURCE% a %DEPLOYMENT_TARGET%
Na uwagę zasługuje zmienna %DEPLOYMENT_SOURCE%. Wskazuje ona na ścieżkę do folderu repozytorium z projektem. W moim przypadku mój projekt nie leży w głównym katalogu repozytorium, więc musiałem dokonać zmiany w ustawieniach podając prawidłowy katalog.

Dodatkowo dodałem ustawienie „command” wskazujące na mój skrypt deploy.cmd. Nie musisz tego wykonywać jeśli ten skrypt będzie umieszczony w głównym katalogu repozytorium:
Azure – deploy.cmd path

#2 Webpack build

Kolejnym krokiem jest dodanie do skryptu deploy.cmd wywołania webpack aby wykonał przetwarzanie naszego projektu. Co istotne chcę aby ten krok został wykonany jeszcze w lokalnym folderze repozytorium a nie w docelowym site/wwwroot gdzie mają znaleźć się tylko wymagane moduły produkcyjne node’a, statyczne pliki html oraz przygotowany przez webpack /dist. Pozostały kod źródłowy nie powinien w tym miejscu się znaleźć.

Całość zamyka się w takim oto wywołaniu:

deploy.cmd

:Deployment
echo Handling node.js deployment.

:: 1. Webpack Build
IF EXIST "%DEPLOYMENT_SOURCE%webpack.config.js" (
echo Installing node modules dev/prod
pushd "%DEPLOYMENT_SOURCE%"
call npm install --only=prod
call npm install --only=dev
echo Running webpack build
call npm run build
popd
IF !ERRORLEVEL! NEQ 0 goto error
echo Finished webpack build
)

  • Sprawdzam czy w folderze projektu znajduje się plik konfiguracyjny webpack.config.js
  • Następnie przystępuje do instalacji paczek node’a – produkcyjnych i deweloperskich. Związane jest to z tym iż package.json dzieli moduły, które są potrzebne do uruchomienia aplikacji (dependencies) od tych wymaganych tylko w środowisku deweloperskim (devDependencies). My potrzebujemy obydwie zależności.
  • następnie uruchamiam skonfigurowany w package.json „build” skrypt. Komenda npm run build
package.json
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
},

Tak wygląda log z wywołania:

Azure – deploy.cmd output

Widać, że został wykorzystany niestandardowy katalog źródłowy, własny skrypt oraz wywołanie instalacji paczek node’a i webpack.

#3 KuduSync

Kolejnym krokiem jest kopiowanie plików z folderu źródłowego do docelowego site/wwwroot. Na tym etapie należy wykluczyć niepotrzebne pliki. Komenda %KUDU_SYNC_CMD% zawiera argument „-i”, który umożliwia wskazanie takich plików/folderów. Domyślnie jest dodany foldery .git oraz skrypty deploy. Resztę dopisujemy wg uznania. Na pewno nie chcę kopiować zainstalowanych na potrzeby webpack paczek node’a oraz plików źródłowych .vue. Możemy także wykluczyć pliki konfiguracyjne takie jak webpack.config.js

:: 2. KuduSync
IF /I "%IN_PLACE_DEPLOYMENT%" NEQ "1" (
call :ExecuteCmd "%KUDU_SYNC_CMD%" -v 50 -f "%DEPLOYMENT_SOURCE%" -t "%DEPLOYMENT_TARGET%" -n "%NEXT_MANIFEST_PATH%" -p "%PREVIOUS_MANIFEST_PATH%" -i ".git;.hg;.deployment;deploy.cmd;node_modules;.vue;webpack.config.js"
IF !ERRORLEVEL! NEQ 0 goto error
)

Podsumowanie

Dzięki własnej implementacji a raczej rozszerzeniu deploy.cmd o wywołanie webpack udało się uzyskać bardzo wygodne rozwiązanie gdzie nie muszę umieszczać w repozytorium wygenerowanych plików przerzucając to na serwer Azure. Głównym problemem, który najbardziej może doskwierać to to, iż teraz musimy niejako utrzymywać dwa środowiska deweloperskie tak aby zarówno build lokalny jak i ten na Azure wykonywały się poprawnie.