From 99641d275ad5cdfb01274b413a1d92b25f8790fb Mon Sep 17 00:00:00 2001 From: khun84 Date: Mon, 11 Oct 2021 17:12:43 +0800 Subject: [PATCH 1/6] Added dockerfile for ruby 231 on ubuntu 20.04 --- .gitignore | 4 +++ README.md | 5 ++++ ruby_231/Dockerfile | 68 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 ruby_231/Dockerfile diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..db370e9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.idea/ +tmp/ +log/ +logs/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..4678af4 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +## Description + +A repo to build Ruby container. Currently support the following versions: + +1. ruby-231 on ubuntu 20.04 \ No newline at end of file diff --git a/ruby_231/Dockerfile b/ruby_231/Dockerfile new file mode 100644 index 0000000..909b019 --- /dev/null +++ b/ruby_231/Dockerfile @@ -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 libssl1.0-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"] From cf413fc55d148c56e48a1d98be4c749d5210a8c4 Mon Sep 17 00:00:00 2001 From: khun84 Date: Mon, 11 Oct 2021 17:29:01 +0800 Subject: [PATCH 2/6] Comment out scripts related to building ruby from scratch --- ruby_231/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ruby_231/Dockerfile b/ruby_231/Dockerfile index 909b019..277d5bf 100644 --- a/ruby_231/Dockerfile +++ b/ruby_231/Dockerfile @@ -6,11 +6,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends bzr git mercuri 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 libssl1.0-dev software-properties-common && \ + 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/* +#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 && \ From adc1ba9281d2efd6dab137fdf975d3acf569a1c6 Mon Sep 17 00:00:00 2001 From: khun84 Date: Tue, 12 Oct 2021 09:47:03 +0800 Subject: [PATCH 3/6] Added more detail to documentation --- README.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4678af4..defa3f6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ ## Description -A repo to build Ruby container. Currently support the following versions: +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. -1. ruby-231 on ubuntu 20.04 \ No newline at end of file +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. From ab81fa0f7b353ecfa279fae001c856c4eff9ae7c Mon Sep 17 00:00:00 2001 From: khun84 Date: Tue, 12 Oct 2021 16:17:59 +0800 Subject: [PATCH 4/6] Setup build time helper scripts and introduce branch config --- config.yml | 11 ++++ ruby_231/{ => ubuntu2004}/Dockerfile | 0 scripts/build_docker_path | 16 ++++++ scripts/build_tag | 76 ++++++++++++++++++++++++++++ scripts/util.rb | 19 +++++++ 5 files changed, 122 insertions(+) create mode 100644 config.yml rename ruby_231/{ => ubuntu2004}/Dockerfile (100%) create mode 100755 scripts/build_docker_path create mode 100755 scripts/build_tag create mode 100644 scripts/util.rb diff --git a/config.yml b/config.yml new file mode 100644 index 0000000..923f638 --- /dev/null +++ b/config.yml @@ -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 diff --git a/ruby_231/Dockerfile b/ruby_231/ubuntu2004/Dockerfile similarity index 100% rename from ruby_231/Dockerfile rename to ruby_231/ubuntu2004/Dockerfile diff --git a/scripts/build_docker_path b/scripts/build_docker_path new file mode 100755 index 0000000..2b778d8 --- /dev/null +++ b/scripts/build_docker_path @@ -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 diff --git a/scripts/build_tag b/scripts/build_tag new file mode 100755 index 0000000..f1f23fe --- /dev/null +++ b/scripts/build_tag @@ -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 diff --git a/scripts/util.rb b/scripts/util.rb new file mode 100644 index 0000000..0b5734e --- /dev/null +++ b/scripts/util.rb @@ -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 From d6a6c97c21b5a40a003e1acad0231b4cff22e612 Mon Sep 17 00:00:00 2001 From: khun84 Date: Tue, 12 Oct 2021 16:19:13 +0800 Subject: [PATCH 5/6] Setup ci integration config --- .circleci/config.yml | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 .circleci/config.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000..78904dc --- /dev/null +++ b/.circleci/config.yml @@ -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)\/.*/ From 0a27846d06d6ed3a4ee4f55535e7f38f76e81688 Mon Sep 17 00:00:00 2001 From: khun84 Date: Tue, 12 Oct 2021 16:19:24 +0800 Subject: [PATCH 6/6] Update documentation --- README.md | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/README.md b/README.md index defa3f6..8819d34 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,40 @@ Currently support the following versions: ## 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. \ No newline at end of file