# Builds tonalmine/images/icons.svg from tools/icon_map.yml. # Usage (from repo root): ruby tools/build_icons.rb # Ruby stdlib only. Downloads are cached in tools/.cache/. require 'yaml' require 'net/http' require 'uri' require 'fileutils' CORE_ICONS = 'https://raw.githubusercontent.com/redmine/redmine/7.0-stable/config/icon_source.yml' SYMBOLS = 'https://raw.githubusercontent.com/google/material-design-icons/master/symbols/web' ROOT = File.expand_path('..', __dir__) CACHE = File.join(ROOT, 'tools/.cache') OUT = File.join(ROOT, 'tonalmine/images/icons.svg') def fetch(url) res = Net::HTTP.get_response(URI(url)) res.is_a?(Net::HTTPSuccess) ? res.body : nil end def fail_with(message, items) warn "#{message}:\n #{items.join("\n ")}" exit 1 end # 1. Map must cover exactly the core icon names. core_yml = fetch(CORE_ICONS) or fail_with('Could not fetch', [CORE_ICONS]) core = YAML.safe_load(core_yml).map { |e| e['name'] }.uniq map = YAML.load_file(File.join(ROOT, 'tools/icon_map.yml')) names = map.map { |e| e['name'] } dupes = names.select { |n| names.count(n) > 1 }.uniq fail_with('Duplicate names in icon_map.yml', dupes) if dupes.any? missing = core - names extra = names - core fail_with('Missing from icon_map.yml', missing) if missing.any? fail_with('Not in Redmine icon_source.yml (extra)', extra) if extra.any? # 2. Download (or read cached) SVGs. FileUtils.mkdir_p(CACHE) not_found = [] symbols = map.sort_by { |e| e['name'] }.map do |e| file = "#{e['symbol']}#{e['fill'] ? '_fill1' : ''}_24px.svg" cached = File.join(CACHE, file) unless File.exist?(cached) svg = fetch("#{SYMBOLS}/#{e['symbol']}/materialsymbolsoutlined/#{file}") next not_found << "#{e['name']} -> #{file}" unless svg File.write(cached, svg) end paths = File.read(cached).scan(/]*\bd="([^"]+)"/).flatten fail_with('No found in', [cached]) if paths.empty? body = paths.map { |d| %() }.join %(#{body}) end fail_with('Material Symbol not found', not_found) if not_found.any? # 3. Write the sprite. File.write(OUT, <<~SVG) #{symbols.join("\n")} SVG puts "Wrote #{symbols.size} icons to #{OUT.sub("#{ROOT}/", '')}"