Automatyczny deploy aplikacji Angular na Heroku

Heroku logo

Proces deploymentu, to dla deweloperów temat rzeka. Mamy obecnie wiele możliwości oraz wiele gotowych rozwiązań. W tym wpisie zademonstruję jak wykonać deploy aplikacji napisanej przy użyciu frameworka Angular na Heroku.

Przygotowanie

Na początku musimy trochę zmodyfikować package.json, który został wygenerowany za pomocą Angular CLI. Nasza aplikacja będzie budowana bezpośrednio na serwerze Heroku, dlatego musimy przenieść wymagane zależności z devDependencies do dependencies. W moim przypadku są to:

  • "@angular/cli": "^1.0.1"
  • "@angular/compiler-cli": "^4.1.0"
  • "typescript": "^2.3.1"

Dodatkowo, zadeklarujmy jaką wersję node i npm Heroku powinien wykorzystać do budowania aplikacji. Określamy to w następujący sposób:

"engines": {
  "node": "7.5.0",
  "npm": "4.1.2"
},

W przypadku nie zadeklarowania wersji node i  npm, Heroku użyje domyślnych. W chwili obecnej są to: node 6.10.3 i npm 3.10.10. Lokalnie korzystałem z node 7.5.0 i npm 4.1.2, więc aby wystrzec się błędów i różnic przy instalacji oraz kompilowaniu, zdecydowałem się na zadeklarowanie wersji, z których korzystam.

Budowanie aplikacji

Jeżeli przygotowaliśmy aplikację, przetestujmy czy uda się nam ją zbudować lokalnie. Do tego celu wykorzystamy komendę:

ng build --prod --aot

Parametr --prod oznacza wersję produkcyjną, natomiast --aot Ahead-of-time.

Polecam zerknąć na dokumentację Angular CLI, gdzie znajdziemy wszystkie atrybuty możliwe do wykorzystania podczas budowania aplikacji.

Jeżeli wszystko przebiegnie pomyślnie, zostanie utworzony folder dist/, a w nim znajdować się będzie kod wynikowy. Dodatkowo możemy przetestować poprawność budowania używając serwera http.

Angular app build

Na koniec tego etapu umieszczamy komendę do budowania aplikacji do skryptów. Zależy nam na tym, by Heroku automatycznie wykonało budowanie, więc skrypt nazywamy postinstall.

"scripts": {
  ...
  "postinstall": "ng build --prod --aot"
},

Utworzenie serwera

Do uruchomienia naszej aplikacji na Heroku wykorzystamy niezawodnego Express.js. Instalujemy framework poleceniem:

npm install express --save

Po pomyślnej instalacji, tworzymy prosty skrypt, który wypuści naszą aplikację na domyślny port Heroku czyli 8080:

const express = require('express');
const app = express();

app.use(express.static(__dirname + '/dist'));
app.listen(process.env.PORT || 8080);

Ostatnią czynność jaką musimy wykonać w tym etapie, to podmiana w package.json skryptu start:

"scripts": {
  ...
  "start": "node server.js"
},

Aplikacja na Heroku

Utworzenie aplikacji na Heroku zajmuje chwilę. Na początku wybieramy nazwę aplikacji oraz region gdzie nasza aplikacja będzie uruchomiona.

Heroku create app

Następnie w zakładce Deploy, mamy możliwość wybrania sposobu, w jaki nasza aplikacja ma być dystrybuowana. Mając repozytorium aplikacji w serwisie GitHub, zdecydowałem się na wybranie właśnie tego sposobu.

Heroku deploy app

Teraz możemy zdecydować, czy nasza aplikacja ma być automatycznie deployowana czy też nie. Nic nie stoi na przeszkodzie, by wykonać ręczny deploy w dowolnym momencie.

Heroku automatic deploys

Po wykonanym deployu możemy przejrzeć logi z budowania i uruchomić naszą aplikację.

Heroku log Shopping Manager Web

GIT – submodules – rozdzielenie repozytoriów

GIT logo

Jednym z założeń Shopping Manager było stworzenie aplikacji restowej – backend napisany w Go oraz frontend utworzony przy pomocy Angular2. Jako, że jest to również aplikacja konkursowa, na tegoroczną edycję Daj Się Poznać, zobligowany byłem całość umieścić w jednym repozytorium.

Proces dewelopmentu nie był przez to utrudniony. Wszak pracę nad aplikacją wykonuję cały czas sam. Nie zbaczając z toru, tkwiłem jednak w tym błędnym rozwiązaniu.

Moment krytyczny

Przyszedł jednak dzień, że aplikacja powinna opuścić localhosta. Wtedy też, chcąc nie chcąc, musiałem rozdzielić backend od frontendu.

Z pomocą przyszedł niezawodny GIT. Oferuje on rozwiązanie submodules, które doskonale nadaje się do rozwiązania wszystkich problemów. Pierwsze zdania z dokumentacji znakomicie odzwierciedlają mój problem:

It often happens that while working on one project, you need to use another project from within it. Perhaps it’s a library that a third party developed or that you’re developing separately and using in multiple parent projects. A common issue arises in these scenarios: you want to be able to treat the two projects as separate yet still be able to use one from within the other.

Dwa nowe repozytoria

Dotychczasowa struktura wyglądała następująco:

GIT - Shopping Manager

By rozdzielić repozytoria, trzeba wpierw założyć nowe. Dlatego stworzyłem odpowiednio shopping-manager-api i shopping-manager-web:

Po odpowiednim sformatowaniu plików i przygotowaniu niezależnych repozytoriów, możemy je umieścić w starym. Aby to zrobić należy posłużyć się komendą:

git submodule add nazwa_repozytorium

Dodatkowo zależało mi, by struktura projektu została taka sama. Dlatego po nazwie repozytorium podałem nazwę folderu, w którym submodule miał zostać stworzony:

git submodule add git@github.com:dawidrylko/shopping-manager-api.git api

Repozytorium zostało sklonowane do wskazanego katalogu. Został także stworzony plik .gitmodules, którego struktura przedstawia się następująco:

Jeżeli teraz spojrzymy na główne repozytorium, możemy zauważyć, że wygląd folderu się zmienił. Dodatkowo obok folderu widnieje commit na który wskazuje submodule.

GIT submodules

Aktualizacja

Repozytoria, które zawarte są w submodułach, wskazują na odpowiedniego commita. Aby zaaktualizować submodules wykonujemy komendę:

git submodule update --remote --merge