Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
version: 2.1
jobs:
build:
working_directory: ~/project
docker:
# - image: docker:18.06.1-ce-git
- image: cimg/ruby:2.7.3
steps:
- checkout
- setup_remote_docker
- run:
name: Check branch build config
command: ./scripts/build_tag --check $CIRCLE_BRANCH
- run:
name: Setup custom environment variables
command: |
echo "export RUBY_REPO=${DOCKER_USERNAME}/docker-ruby" >> $BASH_ENV
echo "export BRANCH_ENV=$(echo $CIRCLE_BRANCH | grep -oE --color=never '^(masters|test)\/')" >> $BASH_ENV
echo "export FLAVOUR_TAG=$(./scripts/build_tag ${CIRCLE_BRANCH})" >> $BASH_ENV
echo "export DOCKERFILE_PATH=$(./scripts/build_docker_path ${CIRCLE_BRANCH})" >> $BASH_ENV
echo "export FORMATTED_CIRCLE_BRANCH=$(echo ${CIRCLE_BRANCH} | tr '/' '_')" >> $BASH_ENV
- run:
name: Build Docker image
command: |
source $BASH_ENV
docker build -t "$RUBY_REPO:${FORMATTED_CIRCLE_BRANCH}_build${CIRCLE_BUILD_NUM}" -t "$RUBY_REPO:${FLAVOUR_TAG}_build${CIRCLE_BUILD_NUM}" ./${DOCKERFILE_PATH}
# - run:
# name: Add sandbox tag
# command: |
# source $BASH_ENV
# if [ "$BRANCH_ENV" == "test/" ]
# then
# echo Adding sandbox tag...
# docker tag $RUBY_REPO:$CIRCLE_BUILD_NUM $RUBY_REPO:$CIRCLE_BUILD_NUM-sandbox
# fi
- deploy:
name: Push Docker image
command: |
source $BASH_ENV
echo Logging in Docker Hub...
echo $DOCKER_PASSWORD | docker login --username $DOCKER_USERNAME --password-stdin
docker push $RUBY_REPO:${FORMATTED_CIRCLE_BRANCH}_build${CIRCLE_BUILD_NUM}
docker push $RUBY_REPO:${FLAVOUR_TAG}_build${CIRCLE_BUILD_NUM}
workflows:
version: 2.1
build-deploy:
jobs:
- build:
filters:
branches:
only: /^(masters|test)\/.*/
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.idea/
tmp/
log/
logs/
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
## Description

A repo to build Ruby container. The container image only contains essential apt package. For dated Ruby (< 2.4.0) that is built on top of recent ubuntu release (>= 20.04), some ruby version manager like `rvm` will be used as there will be build error due to package incompatibility when we build Ruby from scratch.

Currently support the following versions:

1. ruby-231 on ubuntu 20.04

## Things to note

1. For Ruby containers that rely on `RVM`, login bash command is required should you want to run any Ruby command in an non-interactive shell session. In fact, this is the most common run pattern as most Ruby processes are started by doing `bundle exec rails server` or `bundle exec sidekiq start` and etc during deployment. These commands will not work in `RVM` managed Ruby unless you fired it in this fashion, `bash -lc "bundle exec sidekiq start"`. This is due to the fact that `RVM` Ruby version selector is implemented as a bash function and it will be loaded from the `.bash_profile` under login shell session. So if your Ruby process is required to be run under different user (other than `root`), please check if the dedicated user can invoke the `rvm` function after login.

## Branching strategy

As we are managing different flavours of Ruby in a single repo, we will be managing those flavours using branch name.

Say we want to create a Ruby build for `ruby-250-ubuntu2004`. The following steps are required:

1. Create a dev branch with sensible name such as `ruby-250-ubuntu2004`
2. Create a test branch named as `test/ruby-250-ubuntu2004`
3. Create a master branch named as `masters/ruby-250-ubuntu2004`
4. Create a PR the dev branch to `masters/ruby-250-ubuntu2004`
5. Add the new build config to the config file. See [CI](#ci) section for how to configure a CI for a new flavour.
6. Start developing and generate a test build by merging into `test/ruby-250-ubuntu2004`.
7. Once its tested, merge the PR.
8. Repeat step 1-7 if you need to deliver more patch to the same flavour. (config setup only needs to be done once when the flavour is first created)

## Image tagging convention

Image tag is constructed by joining all the namespaces with `_`. Say we want to want a tag to carry the information of ruby version, os version, the tag will be something like `ruby-231_ubuntu-2004`.

## CI

To create a new ruby flavour build, create a properly namespaced directory and put your `Dockerfile` inside there. You don't have to prepend `masters` or `tests` to the branch name as this will be handled during build time.

```yaml
"ruby-231/ubuntu2004": # => this is the branch name
target: "ruby_231/ubuntu2004" # => this is the target path of the Dockerfile
ruby_version: ruby-231 # => these are the meta info of the container image
os_version: ubuntu-2004
ruby_manager: rvm
tags: # => these are the tags that you want to use for the build image. The values will be extracted from the parent level
- ruby_version
- os_version
- ruby_manager
```

Automated CI is setup branches with name starting with `masters/` or `test/`. For example, changes in branch `masters/foo` will trigger new build, but changes in branch `foo` will not.
11 changes: 11 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
# root keys are branch name and target is where the Dockerfile is located
"ruby-231/ubuntu2004":
target: "ruby_231/ubuntu2004"
ruby_version: ruby-231
os_version: ubuntu-2004
ruby_manager: rvm
tags:
- ruby_version
- os_version
- ruby_manager
68 changes: 68 additions & 0 deletions ruby_231/ubuntu2004/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
FROM ubuntu:20.04
SHELL ["/bin/bash", "-Eeuxo", "pipefail", "-c"]
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl wget && rm -rf /var/lib/apt/lists/*
RUN apt-get update && apt-get install -y --no-install-recommends bzr git mercurial openssh-client subversion procps && rm -rf /var/lib/apt/lists/*

ENV DEBIAN_FRONTEND noninteractive
RUN echo 'deb http://security.ubuntu.com/ubuntu bionic-security main' >> /etc/apt/sources.list
RUN apt-get update && apt-cache policy libssl1.0-dev && \
apt-get install -y --no-install-recommends autoconf automake bzip2 file g++ gcc imagemagick libbz2-dev libc6-dev libcurl4-openssl-dev libdb-dev libevent-dev libffi-dev libgdbm-dev libgeoip-dev libglib2.0-dev libjpeg-dev libkrb5-dev liblzma-dev libmagickcore-dev libmagickwand-dev libmysqlclient-dev libncurses-dev libpng-dev libpq-dev libreadline-dev libsqlite3-dev libssl-dev libtool libwebp-dev libxml2-dev libxslt-dev libyaml-dev make patch xz-utils zlib1g-dev software-properties-common && \
rm -rf /var/lib/apt/lists/*

# Ruby 231 needs the 1.0 openssl while ubuntu 20 use openssl 1.1
#RUN apt-cache policy libssl1.0-dev && apt-install libssl-dev=1.0.2n-1ubuntu5.7 && rm -rf /var/lib/apt/lists/*

# Install RVM
RUN apt-add-repository -y ppa:rael-gc/rvm && \
apt-get update -y -qq && \
apt-get install -y -qq rvm && \
rm -rf /var/lib/apt/lists/*

# Set timezone for tzdata
ENV TZ=Etc/UTC
RUN ln -fs /usr/share/zoneinfo/$TZ /etc/localtime && \
echo $TZ > /etc/timezone

###############################################################################
# OPTION 1 (have openssl issue)
# Build ruby 2.3.1 from scratch
#ENV RUBY_VERSION=2.3.1
#ENV RUBY_VERSION=2.3.1
#ENV RUBY_DOWNLOAD_SHA256=b87c738cb2032bf4920fef8e3864dc5cf8eae9d89d8d523ce0236945c5797dcd
#ENV RUBYGEMS_VERSION=2.6.8
#
#RUN /bin/sh -c set -ex && \
# buildDeps='bison libgdbm-dev ruby' && \
# apt-get update && \
# apt-get install -y --no-install-recommends $buildDeps && \
# rm -rf /var/lib/apt/lists/* && \
# wget -O ruby.tar.gz "https://cache.ruby-lang.org/pub/ruby/$RUBY_MAJOR/ruby-$RUBY_VERSION.tar.gz" && \
# echo "$RUBY_DOWNLOAD_SHA256 *ruby.tar.gz" | sha256sum -c - && \
# mkdir -p /usr/src/ruby && \
# tar -xzf ruby.tar.gz -C /usr/src/ruby --strip-components=1 && \
# rm ruby.tar.gz && \
# cd /usr/src/ruby && \
# { echo '#define ENABLE_PATH_CHECK 0'; echo; cat file.c; } > file.c.new && \
# mv file.c.new file.c && \
# autoconf && \
# ./configure --disable-install-doc && \
# make -j"$(nproc)" && \
# make install && \
# apt-get purge -y --auto-remove $buildDeps && \
# cd / && \
# rm -r /usr/src/ruby && \
# gem update --system "$RUBYGEMS_VERSION"
#
#ENV BUNDLER_VERSION=1.16.6
#RUN /bin/sh -c gem install bundler --version "$BUNDLER_VERSION"
#ENV GEM_HOME=/usr/local/bundle
#ENV BUNDLE_PATH=/usr/local/bundle BUNDLE_BIN=/usr/local/bundle/bin BUNDLE_SILENCE_ROOT_WARNING=1 BUNDLE_APP_CONFIG=/usr/local/bundle
#ENV PATH=/usr/local/bundle/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
#RUN /bin/sh -c mkdir -p "$GEM_HOME" "$BUNDLE_BIN" && chmod 777 "$GEM_HOME" "$BUNDLE_BIN"

###################################################################################
# OPTION 2: Install Ruby 2.3.1 through RVM
RUN bash -lc 'rvm install ruby-2.3.1' && \
bash -lc 'rvm cleanup all' && \
bash -lc 'rvm --default use ruby-2.3.1'
CMD ["bash", "-lc", "irb"]
16 changes: 16 additions & 0 deletions scripts/build_docker_path
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/usr/bin/env ruby

require 'yaml'
require_relative './util'
require 'optparse'

original_branch = ARGV[0]

raise "Branch name must be given" if Util.blank?(original_branch)

branch = original_branch.gsub(/^(masters|test)\//, '')
config = YAML.load_file('config.yml')
build_path = config.dig(branch, 'target')

raise "Dockerfile not found for #{original_branch}" if Util.blank?(build_path)
puts build_path
76 changes: 76 additions & 0 deletions scripts/build_tag
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env ruby

require 'yaml'
require_relative './util'
require 'optparse'

Options = {}
parser = OptParse.new do |opt|
opt.on('-c', '--check', 'Validate the branch config') do |val|
Options[:check] = val
end

opt.on('-a', '--all', 'Perform action on all branches in the config') do |val|
Options[:all] = val
end
end

parser.parse! ARGV
branch = ARGV[0]

Config = YAML.load_file('config.yml').freeze
TAG_DELIMITER = '_'.freeze

def info(msg = '')
puts msg if Options[:check]
end

def build_tag(branch_name, _args = {})
branch = branch_name.gsub(/^(masters|test)\//, '')

# @type [Hash]
branch_config = Config[branch]

info "\nBranch config for #{branch}"
info "-------------"
info branch_config

raise 'Branch build config not found' unless Util.present?(branch_config)
raise 'Branch build target not found' unless Util.present?(branch_config['target'])

# check tags
# @type [Array]
tags = branch_config['tags']
required_tags = ['ruby_version', 'os_version'].freeze

# Tags presence
raise 'No tags are configured!' if Util.blank?(tags)

# check required tags presence
required_tags.each do |tag|
raise "Required tag '#{tag}' is missing" unless tags.include? tag
end

# check tags value presence
joined_tag = tags.each_with_object([]) do |tag, memo|
raise "#{tag} is configured but the value is empty!" if Util.blank? branch_config[tag]
memo << branch_config[tag]
end.join(TAG_DELIMITER)

info "\nFinalised tag:"
info '--------------'
puts joined_tag
joined_tag
end

info "Checking branch config..."

if Options[:all]
Config.keys.each do |br|
next if br == 'version'
build_tag(br, check: Options[:check])
end
else
raise "Branch name must be given" if Util.blank?(branch)
build_tag branch
end
19 changes: 19 additions & 0 deletions scripts/util.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Util
def self.present?(val)
return false if val.nil?
case val
when String
!val.empty?
when Array
val.length > 0
when Hash
val.keys.length > 0
else
true
end
end

def self.blank?(val)
!present?(val)
end
end