#!/usr/bin/env ruby
# frozen_string_literal: true

# this script creates a build matrix for github actions from the claimed supported platforms and puppet versions in metadata.json

require 'json'

# Sets an output variable in GitHub Actions. If the GITHUB_OUTPUT environment
# variable is not set, this will fail with an exit code of 1 and
# send an ::error:: message to the GitHub Actions log.
# @param name [String] The name of the output variable
# @param value [String] The value of the output variable

def set_output(name, value)
  # Get the output path
  output = ENV.fetch('GITHUB_OUTPUT')

  # Write the output variable to GITHUB_OUTPUT
  File.open(output, 'a') do |f|
    f.puts "#{name}=#{value}"
  end
rescue KeyError
  puts '::error::GITHUB_OUTPUT environment variable not set.'
  exit 1
end

IMAGE_TABLE = {
  'RedHat-7' => 'rhel-7',
  'RedHat-8' => 'rhel-8',
  'RedHat-9' => 'rhel-9',
  'RedHat-9-arm' => 'rhel-9-arm64',
  'SLES-12' => 'sles-12',
  'SLES-15' => 'sles-15',
  'Windows-2012 R2' => 'windows-2012-r2-core',
  'Windows-2016' => 'windows-2016',
  'Windows-2019' => 'windows-2019-core'
}.freeze

DOCKER_PLATFORMS = [
  'CentOS-6',
  'CentOS-7',
  'CentOS-8',
  'Debian-10',
  'Debian-8',
  'Debian-9',
  'OracleLinux-6',
  'OracleLinux-7',
  'Scientific-6',
  'Scientific-7',
  'Ubuntu-14.04',
  'Ubuntu-16.04',
  'Ubuntu-18.04',
  'Ubuntu-20.04'
].freeze

# This table uses the latest version in each collection for accurate
# comparison when evaluating puppet requirements from the metadata
COLLECTION_TABLE = {
  '6.21.0' => 'puppet6-nightly',
  '7.4.0' => 'puppet7-nightly'
}.freeze

matrix = {
  platform: [],
  collection: []
}

metadata = JSON.parse(File.read('metadata.json'))
# Set platforms based on declared operating system support
metadata['operatingsystem_support'].sort_by { |a| a['operatingsystem'] }.each do |sup|
  os = sup['operatingsystem']
  sup['operatingsystemrelease'].sort_by(&:to_i).each do |ver|
    image_key = "#{os}-#{ver}"
    if IMAGE_TABLE.key? image_key
      matrix[:platform] << IMAGE_TABLE[image_key]
    elsif DOCKER_PLATFORMS.include? image_key
      puts "Expecting #{image_key} test using docker on travis"
    else
      puts "::warning::Cannot find image for #{image_key}"
    end
  end
end

# Set collections based on puppet version requirements
if metadata.key?('requirements') && metadata['requirements'].length.positive?
  metadata['requirements'].each do |req|
    next unless req.key?('name') && req.key?('version_requirement') && req['name'] == 'puppet'

    ver_regexp = /^([>=<]{1,2})\s*([\d.]+)\s+([>=<]{1,2})\s*([\d.]+)$/
    match = ver_regexp.match(req['version_requirement'])
    if match.nil?
      puts "::warning::Didn't recognize version_requirement '#{req['version_requirement']}'"
      break
    end

    cmp_one, ver_one, cmp_two, ver_two = match.captures
    reqs = ["#{cmp_one} #{ver_one}", "#{cmp_two} #{ver_two}"]

    COLLECTION_TABLE.each do |key, val|
      matrix[:collection] << val if Gem::Requirement.create(reqs).satisfied_by?(Gem::Version.new(key))
    end
  end
end

# Set to defaults (all collections) if no matches are found
matrix[:collection] = COLLECTION_TABLE.values if matrix[:collection].empty?

# Just to make sure there aren't any duplicates
matrix[:platform] = matrix[:platform].uniq.sort
matrix[:collection] = matrix[:collection].uniq.sort

set_output('matrix', JSON.generate(matrix))
puts "Created matrix with #{matrix[:platform].length * matrix[:collection].length} cells."
