Skip to content

Instantly share code, notes, and snippets.

@luizkowalski
Last active August 30, 2024 18:46
Show Gist options
  • Save luizkowalski/6e7eb3b318dacc01c80754d2599f47ee to your computer and use it in GitHub Desktop.
Save luizkowalski/6e7eb3b318dacc01c80754d2599f47ee to your computer and use it in GitHub Desktop.
Check if there are any new images available for Kamal accessories
#!/usr/bin/env ruby
# frozen_string_literal: true
# Lives in .kamal/update
require "json"
require "net/http"
require "uri"
require "yaml"
CONFIG = YAML.load_file("config/deploy.yml")
ACCESSORIES = CONFIG["accessories"]
MAX_RESULTS = 30
UNSUPPORTED = ["ghcr.io"].freeze
# These suffixes are just different versions of the same image.
# When it comes to versioning, we only care about the version number.
def normalize_tag(tag)
tag.sub(/^v|-ubuntu.*|-alpine.*|-bookworm.*|-bullseye.*/, "")
end
def scrape_tags(repository, service)
url, tags_under_key = case service
when "dockerhub"
[URI("https://hub.docker.com/v2/repositories/#{repository}/tags?page_size=#{MAX_RESULTS}"), "results"]
when "quay"
[URI("https://quay.io/api/v1/repository/#{repository}/tag/"), "tags"]
end
response = Net::HTTP.get_response(url)
if response.is_a?(Net::HTTPSuccess)
data = JSON.parse(response.body)
data[tags_under_key].map { |tag| tag["name"] } # rubocop:disable Rails/Pluck
else
puts "💣 Error: #{response.code}\n\n"
end
end
def scrape_google_registry(repository)
response = Net::HTTP.get_response(URI("https://gcr.io/v2/#{repository}/tags/list"))
if response.is_a?(Net::HTTPSuccess)
data = JSON.parse(response.body)
data["tags"]
else
puts "💣 Error: #{response.code}\n\n"
end
end
ACCESSORIES.each_value do |config| # rubocop:disable Metrics/BlockLength
image, current_tag = config["image"].split(":")
next puts "⏭️ Skipping #{image}, no tag specified\n\n" if current_tag.nil?
unsupported_message = <<~MESSAGE
😓 Skipping #{image}, not supported...yet
You might want to manually check for updates at 🛜 https://#{image}
MESSAGE
next puts unsupported_message if image.start_with?(*UNSUPPORTED)
repository = image.sub(%r{lscr.io/|quay.io/|gcr.io/}, "").then do |repo|
repo.include?("/") ? repo.split("/") : ["library", repo]
end.join("/")
puts "👀 Checking image updates for #{repository} (Current version: #{current_tag})"
tags = case image
when /quay.io/
scrape_tags(repository, "quay")
when /gcr.io/
scrape_google_registry(repository)
else
scrape_tags(repository, "dockerhub")
end
local_tag = normalize_tag(current_tag)
tags.each do |tag|
upstream_tag = normalize_tag(tag)
next unless Gem::Version.correct?(upstream_tag)
if Gem::Version.new(local_tag) < Gem::Version.new(upstream_tag) # rubocop:disable Style/IfUnlessModifier
break puts " 🚀 New version available: #{tag}"
end
end
puts "\n"
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment