Fast app generation and deployment

Trevor Hartman

The aim of this guide is to demonstrate the fastest way to create a client-side web app and get it deployed. I'm writing it for myself in preparation for an upcoming Startup Weekend event. We'll keep it CLI-heavy and automation-friendly as opposed to point-and-clicking our way through web dashboards.

Tech

  • GitLab - GitLab's unlimited private repos, amazing CI and free public pages make it an excellent choice for deploying static sites.
  • React - create-react-app is the quickest way to start hacking on React without messing around with Babel and Webpack configs.
  • ES6 - we'll use plain ES6 instead of something fancier like TypeScript, ReasonML, or Elm because the focus is speed and simplicity.

Caveats

  • We'll eschew typical production concerns like performance, security, configuration, and tests since we're aiming to build an MVP in a weekend.
  • Even though a web app will likely need some CRUD we won't spend the time and effort to use an actual database; instead the prototype will use client-side state to simulate how it will work.

Prerequisites

This guide assumes Homebrew but the following packages can be installed by many other means.


_10
brew install yarn
_10
brew install jq
_10
brew install ruby

GitLab

Let's create a GitLab group so we can collaborate with our teammates.


_29
gem install gitlab
_29
export GITLAB_API_ENDPOINT=https://gitlab.com/api/v4
_29
_29
# visit https://gitlab.com/profile/personal_access_tokens and generate a token
_29
echo "Enter your GitLab token"
_29
read -s gitlab_token
_29
export GITLAB_API_PRIVATE_TOKEN=$gitlab_token
_29
_29
gitlab user # make sure we're authenticated
_29
user_id=`gitlab user --json | jq '.result.id'`
_29
echo $gitlab_user_id # this should be an integer
_29
_29
gitlab help create_group
_29
# pick your own unique group name and group path
_29
# these are global on GitLab
_29
group_json=`gitlab create_group startup-weekend-2019 startup-weekend-2019 --json`
_29
echo $group_json | jq
_29
group_id=`echo $group_json | jq '.result.id'`
_29
echo $group_id # should be an integer
_29
_29
# add your teammates (you'll need their GitLab user IDs)
_29
gitlab help add_group_member
_29
# we'll give everyone owner permissions for this exercise
_29
# see https://docs.gitlab.com/ee/api/access_requests.html for docs
_29
access_level=50
_29
gitlab add_group_member $group_id $user_id $access_level
_29
# the above will fail because we're already a member
_29
# instead perform this for gitlab users on your team that you want to add
_29
# you can do this via the web dashboard if you like

Now that our group is setup and members added, let's create the repo under it.


_10
gitlab help create_project
_10
project_json=`gitlab create_project app "{namespace_id: '$group_id'}" --json`
_10
# confirm it succeeded:
_10
echo $project_json

Node.js React App

Let's generate a fresh React app:


_10
yarn global add create-react-app
_10
create-react-app startup-weekend
_10
cd startup-weekend
_10
yarn install

Now configure the git repo we setup in the previous step and push to it:


_10
git remote add origin `echo $project_json | jq -r '.result.ssh_url_to_repo'`
_10
git push -u origin master

Congratulations, your app source is now safe in GitLab. The next step is to deploy!

Deployment on GitLab Pages

First we need to specify the homepage property in package.json so CRA computes the correct asset paths when it builds assets.


_10
group_path=`echo $group_json | jq -r '.result.full_path'`
_10
project_path=`echo $project_json | jq -r '.result.path'`
_10
gitlab_pages_url=`echo https://$group_path.gitlab.io/$project_path`
_10
_10
tmp=$(mktemp)
_10
cat package.json \
_10
| jq --arg homepage $gitlab_pages_url '. + {homepage: $homepage}' > $tmp \
_10
&& mv $tmp package.json
_10
_10
git commit -am 'Add homepage to package.json'

Now we can setup CI. Run this to add a .gitlab-ci.yml to your repo specifying how to build in GitLab CI and deploy to GitLab Pages:


_33
cat << 'EOF' > .gitlab-ci.yml
_33
cache:
_33
untracked: true
_33
key: "$CI_BUILD_REF_NAME"
_33
paths:
_33
- node_modules/
_33
- .yarn-cache
_33
_33
image: node
_33
_33
stages:
_33
- deps
_33
- test
_33
- publish
_33
_33
deps:
_33
stage: deps
_33
script:
_33
- yarn config set cache-folder .yarn-cache
_33
- yarn install --pure-lockfile
_33
_33
pages: # must be named pages to publish to GitLab pages!
_33
stage: publish
_33
script:
_33
- yarn build
_33
# replace public contents with build - GitLab requires a `public` dir
_33
- rm -rf public/*
_33
- mv build/* public
_33
- ls -al public/
_33
artifacts:
_33
paths:
_33
- public
_33
EOF

With this in place, GitLab CI will build and deploy our app on every git push. Let's try it out:


_10
git add .gitlab-ci.yml && git commit -m 'Setup GitLab CI deployments'
_10
git push

Open up the pipelines dashboard and sit back while your app is built and deployed:


_10
open `echo $project_json | jq -r '.result.web_url'`/pipelines

Once it's finished, view your app:


_10
open $gitlab_pages_url

At this point you can start developing your app and have it auto deployed any time someone pushes to master.

Closing thoughts

One of the Startup Weekend resources is $3k worth of credits on Google Cloud Platform. While GCP is an excellent option for real apps, I opted to keep things as minimal as possible, and it ended up being free.

We could very easily extend this further using the Firebase Realtime Database and Firebase Authentication which gets you Google / Facebook / Twitter / GitHub login and a schemaless data store with very little work. These options change nothing from a deployment standpoint as Firebase usage remains purely client side. And since Firebase is a part of the GCP offering you can use those credits.