Last active
November 24, 2021 10:46
-
-
Save terry90/e2c27fb57514cc7d04dc59d60d61d948 to your computer and use it in GitHub Desktop.
Docker build/push script with semantic versioning
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# coding: utf-8 | |
# Colors utils | |
GREY = "\e[38;5;247m".freeze | |
GREEN = "\e[0;92m".freeze | |
YELLOW = "\e[0;93m".freeze | |
CYAN = "\e[0;96m".freeze | |
RED = "\e[0;91m".freeze | |
NC = "\e[0m".freeze # No Color | |
def green(str) | |
"#{GREEN}#{str}#{NC}" | |
end | |
def red(str) | |
"#{RED}#{str}#{NC}" | |
end | |
def cyan(str) | |
"#{CYAN}#{str}#{NC}" | |
end | |
def grey(str) | |
"#{GREY}#{str}#{NC}" | |
end | |
def yellow(str) | |
"#{YELLOW}#{str}#{NC}" | |
end | |
# String utils | |
def unaccent(str) | |
return str.tr( | |
"ÀÁÂÃÄÅàáâãäåĀāĂ㥹ÇçĆćĈĉĊċČčÐðĎďĐđÈÉÊËèéêëĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥĦħÌÍÎÏìíîïĨĩĪīĬĭĮįİıĴĵĶķĸĹĺĻļĽľĿŀŁłÑñŃńŅņŇňʼnŊŋÒÓÔÕÖØòóôõöøŌōŎŏŐőŔŕŖŗŘřŚśŜŝŞşŠšſŢţŤťŦŧÙÚÛÜùúûüŨũŪūŬŭŮůŰűŲųŴŵÝýÿŶŷŸŹźŻżŽž", | |
"AAAAAAaaaaaaAaAaAaCcCcCcCcCcDdDdDdEEEEeeeeEeEeEeEeEeGgGgGgGgHhHhIIIIiiiiIiIiIiIiIiJjKkkLlLlLlLlLlNnNnNnNnnNnOOOOOOooooooOoOoOoRrRrRrSsSsSsSssTtTtTtUUUUuuuuUuUuUuUuUuUuWwYyyYyYZzZzZz" | |
) | |
end | |
# Script begins | |
VERSION_REGEX = /(?<version>[0-9.]+(-(?<letter>[a-z])[a-z]*)?)/.freeze | |
trap("INT") do | |
puts "\r \n#{yellow("Build cancelled")}" | |
exit | |
end | |
def fail_because(reason) | |
puts reason | |
exit | |
end | |
def locate_packages_json | |
Dir.glob("**/package.json").reject { |f| f.include?("node_modules") } | |
end | |
def update_packages_json_version(version) | |
files = locate_packages_json | |
for file in files | |
puts "Updating #{cyan file} to #{green "v#{version}"}" | |
outdata = File.read(file).gsub(/"version": "[0-9.]+",/, "\"version\": \"#{version}\",") | |
File.open(file, "w") do |out| | |
out << outdata | |
end | |
end | |
end | |
def get_random_word(first_letter) | |
dico = `curl -s https://gist.githubusercontent.com/terry90/485a404135651fe5b5da275fec90cf30/raw/cbbe09df668965204837827f9debe130ab12b347/dicobuild` | |
dico = dico.split("\n").group_by { |word| word[0] } | |
words = (1..10).map { unaccent dico[first_letter].sample } | |
puts words.map.with_index { |w, i| "#{i + 1}: #{w}" } | |
puts "Choose a version codename" | |
nb = $stdin.gets.chomp | |
until (1..10).include?(nb.to_i) | |
puts "Choose a number between 1 and 5" | |
nb = $stdin.gets.chomp | |
end | |
words[nb.to_i - 1] | |
end | |
def write_metadata(name, version) | |
File.write(".dockerbuild", "name=#{name}\nversion=#{version}\n") | |
end | |
def get_current_metadata | |
begin | |
dbuild = File.read(".dockerbuild") | |
matches = /name=(?<name>[^\n]+)\nversion=(v)?(?<version>[^\n]+)/.match(dbuild) | |
name = matches[:name] | |
puts "\nApp: #{name}" | |
vmatches = VERSION_REGEX.match(matches[:version]) | |
puts "Current version: #{cyan("v#{matches[:version]}")}" | |
version = vmatches[:version] | |
letter = vmatches[:letter] | |
rescue Errno::ENOENT | |
puts "No .dockerbuild found, creating one..." | |
puts "Please enter image name (e.g eu.gcr.io/project/app) without the version" | |
name = $stdin.gets.chomp | |
puts "Please enter the current version of the app (e.g v0.1.0)" | |
version = $stdin.gets.chomp | |
begin | |
version = VERSION_REGEX.match(version)[:version] | |
rescue StandardError | |
puts "Bad version" | |
exit | |
end | |
letter = nil | |
write_metadata(name, version) | |
end | |
{ name: name, version: version, letter: letter } | |
end | |
def ask_type_of_update | |
loop do | |
puts "Please choose update type:" | |
puts cyan "1: Major" | |
puts green "2: Minor" | |
puts yellow "3: Patch" | |
puts red "4: Test" | |
puts grey "5: Keep current" | |
input = $stdin.gets.to_i | |
if [1, 2, 3, 4, 5].include? input | |
return { 1 => :major, 2 => :minor, 3 => :patch, 4 => :test, 5 => :nothing }[input] | |
end | |
end | |
end | |
def ask_packages_json(version) | |
puts | |
puts "Update packages json ? [Y/n]" | |
for file in locate_packages_json | |
puts grey(file) | |
end | |
update_packages_json_version(version.split("-")[0]) if ["y", "Y"].include?($stdin.gets.strip) | |
end | |
def ask_what_build | |
default = "Dockerfile" | |
builds = Dir.entries("./").reject { |e| !e.include? "Dockerfile" }.map { |e| e.split(".")[1] } | |
return [default] if builds == [nil] | |
loop do | |
puts | |
puts "Please choose build:" | |
puts "1: Both" | |
builds.each_with_index do |e, i| | |
puts "#{i + 2}: #{e ? e.capitalize : "Default"}" | |
end | |
input = $stdin.gets.to_i | |
if (1..(builds.length + 1)).to_a.include? input | |
if input == 1 | |
return builds.map { |e| e ? "Dockerfile.#{e}" : "Dockerfile" } | |
end | |
b = builds[input - 2] | |
return !b ? [default] : ["Dockerfile.#{b}"] | |
end | |
end | |
end | |
def major(vs) | |
[vs[0].to_i + 1, 0, 0].join(".") | |
end | |
def minor(vs) | |
[vs[0], vs[1].to_i + 1, 0].join(".") | |
end | |
def patch(vs) | |
[vs[0], vs[1], vs[2].to_i + 1].join(".") | |
end | |
def testv(vs, word) | |
[[vs[0], vs[1], vs[2].to_i].join("."), word].join("-") | |
end | |
def increment_version(metadata, deploy) | |
return metadata[:version] if deploy == :nothing | |
vs = metadata[:version].split("-")[0].split(".") | |
case deploy | |
when :major | |
major(vs) | |
when :minor | |
minor(vs) | |
when :patch | |
patch(vs) | |
when :test | |
letter = metadata[:letter] ? metadata[:letter].next : "a" | |
word = get_random_word(letter) | |
testv(vs, word) | |
else | |
raise red("ERROR ! Cannot get new version") | |
end | |
end | |
def check_pull() | |
system("git remote update 1>/dev/null") | |
local = `git rev-parse @` | |
base = `git merge-base @ master` | |
remote = `git rev-parse master` | |
if local == remote | |
puts green "Up to date" | |
elsif local == base | |
puts yellow "Need to pull" | |
puts "Do you want to pull (master) ? [Y/n]" | |
system("git pull origin master") if ["y", "Y"].include?($stdin.gets.strip) | |
else | |
puts red "Aborting ! Please sync your git repo" | |
exit 1 | |
end | |
end | |
def commit(version) | |
system("git add .dockerbuild") | |
system("git commit -m \"Bump to #{version}\"") | |
puts "Commit created" | |
puts "Do you want to push (master) ? [Y/n]" | |
system("git push origin master") if ["y", "Y"].include?($stdin.gets.strip) | |
end | |
def build(staging) | |
puts yellow("\n/!\\ ") + red("Staging") + yellow(" build /!\\\n") if staging | |
check_pull | |
metadata = get_current_metadata | |
type_of_update = ask_type_of_update | |
version = increment_version(metadata, type_of_update) | |
ask_packages_json version | |
build_files = ask_what_build | |
for build_file in build_files | |
build_ext = build_file.split(".")[1] | |
metadata[:version] = version | |
name = metadata[:name] | |
name = "#{metadata[:name]}-#{build_ext}" if build_ext | |
name = "#{name}-staging" if staging | |
puts "Building: #{cyan("v#{version}")}#{" #{green build_ext}" if build_ext}..." | |
docker_build_cmd = if staging | |
system("DOCKER_BUILDKIT=1 docker build . -f #{build_file} -t #{name}:v#{version} --build-arg CARGO_FLAGS=--features=staging --build-arg STAGING=true") | |
else | |
system("DOCKER_BUILDKIT=1 docker build . -f #{build_file} -t #{name}:v#{version}") | |
end | |
fail_because("Cannot build docker image") unless docker_build_cmd | |
fail_because("Cannot push docker image") unless system("docker push #{name}:v#{version}") | |
end | |
write_metadata(metadata[:name], metadata[:version]) | |
commit version | |
puts | |
puts "Done ! :)" | |
end | |
def ask_environment | |
puts "Deploy #{cyan "production"} build ? [Y/n]" | |
return !["y", "Y"].include?($stdin.gets.strip) | |
end | |
staging = ask_environment | |
build staging |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment