Files
endoflife-date-release-data/update.rb
Marc Wrobel 701c2899d5 [git] Migrate git method from Ruby to Python (#178)
The main reason for doing this is to have some common code between scripts, so that it is easier to change the JSON schema globally and normalize a few things (such as release order).

The Ruby code was kept as is so we can quickly roll back if necessary.
2023-11-12 20:33:29 +01:00

161 lines
4.7 KiB
Ruby

require 'yaml'
require 'set'
require 'date'
require 'json'
require 'rugged'
require 'liquid'
require 'digest'
require 'net/http'
require 'uri'
WEBSITE_DIR = ARGV[0]
CACHE_DIR = ARGV[1]
OUTPUT_DIR = ARGV[2]
OPTIONAL_PRODUCT = ARGV[3]
# This regex is used in absence of anything else.
# This is more lenient from semver, but disallows MAJOR=0 as well.
# It allows MAJOR.MINOR, MAJOR.MINOR.PATCH and MAJOR.MINOR.PATCH.TINY versions,
# with or without a 'v' prefix, which are quite common.
DEFAULT_VERSION_REGEX = '^v?(?<major>[1-9]\d*)\.(?<minor>\d+)(\.(?<patch>\d+)(\.(?<tiny>\d+))?)?$'
DEFAULT_TAG_TEMPLATE = "{{major}}.{{minor}}{% if patch %}.{{patch}}{% if tiny %}.{{tiny}}{%endif%}{%endif%}"
# extensions.partialClone=true is also set up in the workflow.
# See also https://stackoverflow.com/a/65746233/374236
def fetch_git_releases(repo_dir, config)
pwd = Dir.pwd
`git init --bare #{repo_dir}` unless Dir.exist? repo_dir
Dir.chdir repo_dir
`git fetch --quiet --tags --filter=blob:none --depth=1 "#{config['git']}"`
Dir.chdir pwd
end
def good_tag(tag, config)
config['regex'] ||= DEFAULT_VERSION_REGEX
tag.match?(config['regex'])
end
def render_tag(tag, config)
config['regex'] ||= DEFAULT_SEMVER_REGEX
data = tag.match(config['regex']).named_captures
template = config['template'] ? config['template'] : DEFAULT_TAG_TEMPLATE
Liquid::Template.parse(template).render(data)
end
def get_releases_from_git(repo_dir, auto_config)
data = {}
repo = Rugged::Repository.bare repo_dir
repo.tags.each do |tag|
next unless good_tag(tag.name, auto_config)
tag_proper_name = render_tag(tag.name, auto_config)
# If the tag has an annotation, we get accurate time information
# from the tag annotation "tagger"
begin
if tag.annotated?
# We pick the data from the "tagger" which includes offset information
t = tag.annotation.tagger[:time]
data[tag_proper_name] = t.strftime('%F')
puts "#{tag_proper_name}: #{t.strftime('%F %X %z')}"
else
# In other cases, we de-reference the tag to get the commit
# and use the date of the commit itself
t = tag.target.committer[:time]
data[tag_proper_name] = t.strftime('%F')
puts "#{tag_proper_name}: #{t.strftime('%F %X %z')}"
end
rescue StandardError
puts "::warning No timestamp for #{tag.name}, ignoring"
end
end
return data
end
def get_cache_dir(ecosystem, product, config)
h = Digest::SHA1.hexdigest config['git']
"#{CACHE_DIR}/#{ecosystem}/#{product}_#{h}"
end
def get_output_file(product)
"#{OUTPUT_DIR}/#{product}.json"
end
def generate_commit_message
begin
products = Set.new
ret = nil
msg = ""
r = Rugged::Repository.new '.'
r.status() do |f, s|
p = Pathname.new(f).dirname
if p.to_s === 'releases'
ret = true
product = File.basename(f, '.json')
products << product
old_version_list = JSON.parse(r.blob_at(r.head.target.oid, f).content).keys.to_set
new_version_list = JSON.parse(File.read(f)).keys.to_set
new_versions = (new_version_list - old_version_list)
msg += "#{product}: #{new_versions.join(', ')}\n"
end
end
ret ? "🤖: #{products.join(', ')}\n\n#{msg}": ""
rescue StandardError => e
"🤖: Automatic Update"
end
end
def get_releases(product, config, i)
type = get_update_type(config)
if type == 'oldgit' # replaced by the git.py script, code kept for now to facilitate rollbacks
dir = get_cache_dir('git', product, config)
fetch_git_releases(dir, config)
return get_releases_from_git(dir, config)
elsif type != nil
puts "#{product} handled by #{type} script, skipping"
return {}
else
puts "Undetected method for #{product}"
return {}
end
end
def get_update_type(config)
for i in ['git', 'npm', 'docker_hub', 'distrowatch', 'custom', 'github_releases', 'pypi', 'maven']
return i if config[i]
end
return nil
end
Dir.glob("#{WEBSITE_DIR}/products/*.md").each do |product_file|
data = YAML.safe_load_file product_file, permitted_classes: [Date]
next unless data['auto']
product = File.basename product_file, '.md'
# Only process one product
next if OPTIONAL_PRODUCT && (OPTIONAL_PRODUCT != product)
if data['auto']
release_data = {}
puts "::group::#{product}"
data['auto'].each_with_index do |config, i|
release_data.merge! get_releases(product, config, i)
end
File.open(get_output_file(product), 'w') do |file|
file.write(JSON.pretty_generate(release_data))
end unless release_data.empty?
puts "::endgroup::"
end
end
def github_actions_step_output(msg)
puts "::set-output name=commit_message::#{JSON.dump(msg)}"
end
github_actions_step_output(generate_commit_message)