:sparkles: PWA :sparkles:
Twarz autora bloga

Piotr Kowalski

Organizator WarsawJS Trener YouTuber

Vue.js na Heroku


Podczas hackathonu w jakim brałem udział tydzień temu - Node Knockout 2017, budowałem aplikację, którą - zgodnie z regulaminem wydarzenia - musiałem opublikować na Heroku. Przyzwyczaiłem się już do tego, że zawsze tam lądują moje aplikacje podczas wydarzeń takiego typu.

Spotkały mnie pewne problemy, które rozwiązałem i chciałem się podzielić moim rozwiązaniem. Nie uważam je za idealne. Jednak za wystarczające.

Disclaimer Zdaje sobie z tego sprawę, że wrzucanie katalogu dist do repozytorium jest bez sensowne, ponieważ Git nie powinien wersjonować plików zbundlowanych jednak - jak zawsze - istnieją wyjątki.

Baner reklamujący artykuł

Etap 0 Heroku: Zakładanie konta

  1. Zakładamy konto na: heroku.com

  2. Panel użytkownika: dashboard.heroku.com

    Heroku udostępnia panel dla użytkownika, gdzie można tworzyć nowe kontenery, gdzie uruchomiony jest system operacyjny Linux. Pojedynczy kontener w żargonie firmy nazywa się Dyno

    Więcej na ich temat w pomocy.

Etap 1 Heroku: Przygotowanie środowiska

  1. Tworzymy nową aplikację: NewCreate new app
  2. Wpisujemy nazwę dla naszej aplikacji w pole App name

    Wpisana nazwa jest na sprawdzana “w locie” pod kątem dostępności.

  3. Opcjonalnie: Wybieramy region: Choose a regionEurope

:tada: Aplikacja stworzona! :tada:

Możemy otworzyć aplikację w przeglądarce klikając w przycisk: Open app Naszym oczom powinien ukazać się komunikat:

Heroku | Welcome to your new app!

Etap 2 Stworzenie projektu za pomocą Vue CLI

1. Instalacja

Copy + paste

npm install -g vue-cli

Po instalacji, w terminalu będzie dostępne polecenie vue.

2. Wygenerowanie projektu za pomocą szablonu

Copy + paste

vue init webpack NAZWA_PROJEKTU

Wykorzystujemy jeden w predefiniowanej struktury aplikacji.
W projekcie vue-templates istnieje 6 różnych szablonów:

  • webpack
  • webpack-simple
  • pwa
  • simple
  • browserify
  • browserify-simple

3. Poprawienie wersji

UWAGA: W package.json należy poprawić wersję, z uwagi na problem, który wystąpi podczas instalacji zależności na Heroku:

   "engines": {
-    "node": ">= 4.0.0",
-    "npm": ">= 3.0.0"
+    "node": "8.x",
+    "npm": "5.x"
   },

4. Instalacja zależności

Copy + paste

npm install

W głównym katalogu projektu stworzy się plik package-lock.json, w którym zostaną zapisane wszystkie dane na temat zainstalowanych paczek wraz z ich wersjami.

Stacktrace z błędem

Jeśli nie zaktualizujemy wersji to będziemy świadkami błędu:

remote: -----> Building dependencies
remote:        Installing node modules (package.json + package-lock)
remote:        WARNING: You are likely using a version of node-tar or npm that is incompatible with this version of Node.js.
remote:        Please use either the version of npm that is bundled with Node.js, or a version of npm (> 5.5.1 or < 5.4.0) or node-tar (> 4.0.1) that is compatible with Node.js 9 and above.
remote:        /tmp/build_ab1c9272aa348395c069d548da02c8c4/.heroku/node/bin/node[273]: ../src/node_zlib.cc:437:static void node::{anonymous}::ZCtx::Init(const v8::FunctionCallbackInfo<v8::Value>&): Assertion `args.Length() == 7 && "init(windowBits, level, memLevel, strategy, writeResult, writeCallback," " dictionary)"' failed.
remote:        1: node::Abort() [npm]
remote:        2: node::Assert(char const* const (*) [4]) [npm]
remote:        3: 0x1255aff [npm]
remote:        4: v8::internal::FunctionCallbackArguments::Call(void (*)(v8::FunctionCallbackInfo<v8::Value> const&)) [npm]
remote:        5: 0xb77f9c [npm]
remote:        6: v8::internal::Builtin_HandleApiCall(int, v8::internal::Object**, v8::internal::Isolate*) [npm]
remote:        7: 0x253e476842fd
remote: /app/tmp/buildpacks/19862b8792e84bd8421ded4660b92dfd1c41d92e19ac0b38c90301adc8ae3e0bd512fa01998af18fc2f0d31a157e9c82e8fdceba1a05e5d29adb8dc2bfaf08e1/lib/dependencies.sh: line 111:   273 Aborted                 npm install --unsafe-perm --userconfig $build_dir/.npmrc 2>&1
remote:
remote: -----> Build failed
remote:
remote:        We're sorry this build is failing! You can troubleshoot common issues here:
remote:        https://devcenter.heroku.com/articles/troubleshooting-node-deploys
remote:
remote:        Some possible problems:
remote:
remote:        - Dangerous semver range (>) in engines.node
remote:        https://devcenter.heroku.com/articles/nodejs-support#specifying-a-node-js-version
remote:
remote:        Love,
remote:        Heroku
    

5. Zamieniamy projekt w repozytorium

Copy + paste

git init
git add .
git commit -am "Initial import"

Teraz mamy :two: opcje:

  1. Dodajemy katalog dist/ do repozytorium
  2. Nie dodajemy katalogu dist/ (rekomendowana)

Zobaczmy jak wyglądają obie opcje w realu!

Etap 2Opcja 1 Dodajemy katalog dist/ do repozytorium

1. Budujemy produkcyjną wersję aplikacji w katalogu dist/

Copy + paste

npm run build

2. Wyłączamy ignorowania przez Git-a katalog dist/

Usuwamy linijkę w pliku .gitignore:

  node_modules/
- /dist/
  npm-debug.log*

3. Dodajemy katalog dist/ do repozytorium

Copy + paste

git add .
git commit -am "Add distributed version"

4. Instalacja narzędzia, które będzie serwować katalog dist/

Copy + paste

npm install serve --save

5. Tworzymy zadanie run-script, które uruchomi aplikację

   "scripts": {
-    "start": "npm run dev",
+    "start": "serve dist/",
+    "deploy": "npm run build && git add dist/ && git commit -am 'Deploy' && git push -f heroku master"
   },

Opiszę co robią dodane zadania:

  • start - uruchomi paczkę serve, która będzie serwować katalog dist/, jako główny katalog aplikacji
  • deploy - aby przyspieszyć proces deploymentu warto zrobić takie zadanie, które wykona za nas wszelkie prace związanie ze zbudowaniem wersji produkcyjnej

6. Zapisujemy stan projektu

Copy + paste

git add .
git commit -am "Serve static files"

Aplikacja jest gotowa do pierwszego release-u! :trophy:

Etap 2Opcja 2 Nie dodajemy katalogu dist/ do repozytorium (rekomendowana)

1. Instalujemy paczkę, która będzie serwowała katalog dist/

Dla odmiany wykorzystajmy inną paczkę.
Tym razem niech to będzie http-server.

Copy + paste

npm install http-server --save

2. Dodajemy nowe zadania

    "scripts": {
        "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
        "build": "node build/build.js",
+       "deploy": "git push -f heroku master",
+       "heroku-postbuild": "npm run build",
+       "start": "http-server dist/ -d false -g"
    },

Opiszę nowo dodane polecenia:

  • deploy - zadanie dzięki, któremu zaktualizujemy branch master w Heroku, przez co zostanie zaktualizowany projekt w serwerowni.
  • heroku-postbuild - jest to hook (event handler), który zostanie uruchomiony przez Heroku w celu zbudowania aplikacji.

    Zadanie zostanie uruchomione po instalacji wszystkich zależności.

  • start - uruchamia serwer HTTP, który będzie serwował katalog dist/ jako directoryRoot, czy główny katalog aplikacji

3. Ustawmy zmienną środowiskową na Heroku

W panelu Heroku, w zakładce SettingsConfig Variables. Należy w niej ustawić zmienną:

NPM_CONFIG_PRODUCTION = false

Zmienna jest ustawiona po to, aby podczas procesu budowania projektu Heroku pobrało również zależności developerskie, które są potrzebne do zbudowania aplikacji do katalogu dist/.

Aplikacja jest gotowa do pierwszego release-u! :trophy:

Etap 3 Deployment :zap:

Czas najwyższy, aby opublikować aplikację. Mamy kilka możliwości na deployment aplikacji. Dla mnie interesujące są 2 pierwsze:

  1. Automatyczny deployment z brancha na GitHubie
  2. Manualny deployment po wrzuceniu projektu do zdalnego repozytorium Git, trzymanego na Heroku.

Wybieram opcję numer 2 ponieważ pierwsza jest mniej elastyczna. Chciałbym coś wrzucić na Heroku, ale nie chciałbym publikować tych zmian na GitHuba.

1. Logujemy się do Heroku CLI :unlock:

Copy + paste

heroku login

2. Łączymy repozytorium lokalne ze zdalnym na Heroku :scissors:

Copy + paste

heroku git:remote -a NAZWA_PROJEKTU

3. Wypychamy zmiany na serwer 🚀

Wyślijmy nasze repozytorium na Heroku, aby zainicjować proces deploymentu:

Copy + paste

git push -f heroku master

Odśwież w przeglądarce wcześniej otworzoną stronę, aby zobaczyć:

Domyślny template aplikacji

:confetti_ball: Aplikacja opublikowana! :confetti_ball:

Więcej na temat deploymentu przeczytasz w pomocy.
Aby później deployować aplikację za pomocą jednego polecenia:

Copy + paste

npm run deploy

Jeśli znasz lepszy sposób na deployment napisz komentarz!