Skip to content

Commit fd7474c

Browse files
committed
Add scripts to rule them all assignment and examples
1 parent b8f7a49 commit fd7474c

File tree

9 files changed

+167
-0
lines changed

9 files changed

+167
-0
lines changed

_assignments/assignment_13.md

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Assignment 13 - Scripts To Rule Them All
2+
3+
The [Scripts To Rule Them All](https://github.com/github/scripts-to-rule-them-all) pattern comes from GitHub. It is a set of scripts with normalized names that can be used to get a project up and running in a repeatable and reliable fashion.
4+
5+
The purpose of the assignment is to demonstrate that Docker allows us to easily write scripts that get our environment into a working state. The exact usage and naming of the scripts is ultimately up to you. Coming up with a standard and sticking to it has the advantage that we don't have to look into the README when we switch between projects.
6+
7+
I already prepared a set of scripts for us. You can copy them from `_examples/script`:
8+
```
9+
cp -rp _examples/script ./script
10+
```
11+
12+
We won't go into the inner mechanics of the scripts. They are all pretty straight forward shell scripts that execute `docker` and `docker-compose`. Instead, let's talk about what each script is intended to do:
13+
* `script/bootstrap`: Prepares the runtime environment for the application. This entails building the image and install gems and node modules.
14+
* `script/update` is responsible for updating the dependencies of the application. The script makes sure that Redis and Postgres are up and running, migrates the database, and updates the node modules and gems by calling `bootstrap`.
15+
* `script/setup` is responsible for setting up the whole environment. It should be called after the initial clone or when a full reset of the environment is required. `setup` calls `update`.
16+
* `script/test` will start all services and run the est suite of the application. `test` calls `update` unless the environment variable `FAST` is set.
17+
* `script/server` will start all services and tail the logs of the Rails server and Sidekiq. `server` calls `update` unless the environment variable `FAST` is set.
18+
* `script/console` will start all services and open a Rails console. `console` calls `update` unless the environment variable `FAST` is set.
19+
20+
Whoever needs to work on this project just needs to have **bash and Docker** installed, clone the repository and execute `script/setup`. Since the scripts only execute commands in containers, we know that they **will yield the expected results**! The same pattern can be utilized for other dockerized and non-dockerized applications as well. The great thing is that a developer don't have to understand the inner mechanics of the application to interact with it. They just have to know which script to execute based on what we are trying to achieve.
21+
22+
One tricky thing, when it comes to fully automating the setup of an application, is dealing with external dependencies. They might not being ready to use when you need them. For example it might take Postgres a while to boot. Since we want to script the setup of our application, we need to add steps to make sure our external dependencies are up and running before we use them.
23+
24+
Our set scripts solve this by using a tool called `pg_isready` that is shipped with Postgres. It can be used to determine if Postgres server is up and running. Since we don't want to introduce additional dependencies besides bash and Docker, we are going to run `pg_isready` in container. To do so, we have to install it in our Docker image. Go ahead and add `postgresql-client` to the list of packages that we install with `apk add`:
25+
```
26+
RUN apk add --update --no-cache \
27+
bash \
28+
build-base \
29+
nodejs \
30+
sqlite-dev \
31+
tzdata \
32+
postgresql-dev \
33+
postgresql-client \
34+
yarn
35+
```
36+
You can find a complete example in `_examples/Dockerfile.with_postgres_client`.
37+
38+
To make calling `pg_isready` easier, we are going to wrap it in another script. We are going to call it `wait-for-postgres` and place it in `bin/`. I already prepared the script and you can just copy it:
39+
```
40+
cp -rp _examples/wait-for-postgres ./bin
41+
```
42+
43+
__*Side note*__: We place the `wait-for-postgres` script in `bin/` because it is intended to be executed within the application context. That means we make it part of the application and run it in containers. Everything in `script/` is not part of the actual application. Those are helpers that we use to interact with our application. Everything in `script/` is intended to be run on the Docker Host.
44+
45+
The script uses the `POSTGRES_HOST` environment variable that we pass to the our containers to connect to Postgres. `pg_isready` will yield a unsuccessful exit code if it can't connect to Postgres. The script simply executes `pg_isready` until it returns successful.
46+
47+
Within our Scripts To Rule them All, `wait-for-postgres` is used like this:
48+
```
49+
docker-compose run --rm app bin/wait-for-postgres
50+
```
51+
52+
Go ahead and try the scripts!
53+
54+
# What changed
55+
You can find our changes in the [`scripts_to_rule_them_all`](https://github.com/jfahrer/dockerizing_rails/tree/scripts_to_rule_them_all) branch. [Compare it](https://github.com/jfahrer/dockerizing_rails/compare/spring...scripts_to_rule_them_all) to the previous branch to see what changed.
56+
57+
[Back to the overview](../README.md#assignments)
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
FROM jfahrer/ruby:2.6.3-alpine3.10-ser
2+
3+
RUN apk add --update --no-cache \
4+
bash \
5+
build-base \
6+
nodejs \
7+
sqlite-dev \
8+
tzdata \
9+
postgresql-dev \
10+
postgresql-client \
11+
yarn
12+
13+
RUN gem install bundler:2.0.2
14+
15+
WORKDIR /usr/src/app
16+
17+
COPY Gemfile Gemfile.lock ./
18+
19+
RUN bundle install
20+
21+
COPY package.json yarn.lock ./
22+
23+
RUN yarn install
24+
25+
COPY . .
26+
27+
EXPOSE 3000
28+
29+
ENV PATH=./bin:$PATH
30+
31+
CMD ["rails", "server", "-b", "0.0.0.0", "--pid=/tmp/server.pid"]

_examples/script/bootstrap

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
cd "$(dirname "$0")/.."
3+
4+
set -ex
5+
6+
if [ -z "${SKIP_BUILD}" ]; then
7+
docker-compose build --pull
8+
fi
9+
10+
docker-compose run --rm app bin/bundle
11+
docker-compose run --rm app yarn

_examples/script/console

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
cd "$(dirname "$0")/.."
3+
4+
set -ex
5+
6+
if [ -z "${FAST}" ]; then
7+
script/update
8+
fi
9+
10+
docker-compose up -d
11+
docker-compose exec app rails console

_examples/script/server

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
cd "$(dirname "$0")/.."
3+
4+
set -ex
5+
6+
if [ -z "${FAST}" ]; then
7+
script/update
8+
fi
9+
10+
docker-compose up -d
11+
docker-compose logs -f --tail 50 web sidekiq

_examples/script/setup

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/bin/bash
2+
cd "$(dirname "$0")/.."
3+
4+
set -ex
5+
6+
docker-compose down -v
7+
8+
docker-compose build app
9+
10+
docker-compose up -d pg
11+
12+
docker-compose run --rm app bin/wait-for-postgres
13+
docker-compose run --rm app bin/rake db:create
14+
15+
SKIP_BUILD=1 script/update
16+
17+
docker-compose run --rm app bin/rake db:seed

_examples/script/test

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/bin/bash
2+
cd "$(dirname "$0")/.."
3+
4+
set -ex
5+
6+
if [ -z "${FAST}" ]; then
7+
script/update
8+
fi
9+
10+
docker-compose up -d
11+
docker-compose exec app bin/rspec spec/

_examples/script/update

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#!/bin/bash
2+
cd "$(dirname "$0")/.."
3+
4+
set -ex
5+
6+
script/bootstrap
7+
8+
docker-compose up -d pg redis
9+
docker-compose run --rm app bin/wait-for-postgres
10+
docker-compose run --rm app bin/rake db:migrate
11+
12+
docker-compose up -d

_examples/wait-for-postgres

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/bin/sh
2+
3+
echo -n Waiting for postgres to start...
4+
while ! pg_isready -h ${POSTGRES_HOST:-localhost} > /dev/null; do
5+
sleep 0.5; echo -n .; done
6+
echo done

0 commit comments

Comments
 (0)