A pipeline for a web API
The smallest API
The last project is the one from lesson 47, later in the course, with one addition that makes it testable from a pipeline: a health route that answers without touching the data.
require "json"
require "webrick"
TODOS = []
server = WEBrick::HTTPServer.new(Port: 8000)
server.mount_proc("/health") { |_req, res| res.body = "ok" }
server.mount_proc("/todos") do |req, res|
res["Content-Type"] = "application/json"
res.body = JSON.generate(TODOS)
end
server.start
Two routes are enough for the pipeline: one that says the process is up and one that returns data a request test can assert on.
Run it and check it
Start the server in one terminal, then use the health check from lesson 67 and a plain request for the list:
ruby app.rb
ruby health_check.rb
200 ok
ruby -e 'require "net/http"; puts Net::HTTP.get(URI("http://localhost:8000/todos"))'
[]
An empty list is the correct answer for a fresh process, and it is what a request test asserts before it posts an item. The health check exits zero on the 200; a server that did not start gives a refused connection and a nonzero status.
The pipeline file
The API job adds two things to the pattern from the previous lesson: a service container for the database, and an image that is built after the tests pass:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: "3.4"
bundler-cache: true
- run: bundle exec rake test
image:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -t ci-demo .
- uses: actions/upload-artifact@v4
with:
name: image-tag
path: VERSION
A job that needs a real database declares it as a service, and the tests wait for the port before they connect. In GitLab the same job uses services: with the same image, and the pipeline waits with a health check of its own.
The image build and the registry push are the two steps that need a Docker daemon; on a runner they are ordinary steps, on a laptop they are the reason the last section of this course is read rather than typed.
What to watch for
A service container is not ready when the job starts. Poll the port or
the health route with a timeout, and fail with a message that says which
service did not come up; a test that fails on a connection refused sends
the reader to the wrong file.
Tag the image with the commit, not only with latest. A deploy that pulls
latest cannot say which code is running, and a rollback has nothing to
roll back to.
Next: The same program in Ruby and Python
Article author: Arthur Isaev