105 lines
4.2 KiB
Ruby
Executable File
105 lines
4.2 KiB
Ruby
Executable File
#!/usr/bin/env ruby
|
|
# frozen_string_literal: true
|
|
|
|
require "json"
|
|
require "optparse"
|
|
require_relative "../lib/opencode_compat/runtime_tuple_promoter"
|
|
|
|
root = File.expand_path("..", __dir__)
|
|
command = ARGV.shift
|
|
options = {
|
|
"evidence" => [],
|
|
"previous_evidence" => []
|
|
}
|
|
|
|
parser = OptionParser.new do |opts|
|
|
opts.banner = <<~USAGE
|
|
Usage:
|
|
ruby scripts/promote_runtime_tuple.rb fingerprint --consumer NAME --consumer-commit SHA
|
|
ruby scripts/promote_runtime_tuple.rb promote --consumer NAME --consumer-commit SHA \\
|
|
--status pass --certified-at TIMESTAMP --evidence evidence/FILE.json \\
|
|
[--previous-status pass --previous-certified-at TIMESTAMP \\
|
|
--previous-evidence evidence/FILE.json] [--dry-run]
|
|
USAGE
|
|
|
|
opts.on("--consumer NAME") { |value| options["consumer"] = value }
|
|
opts.on("--consumer-commit SHA") { |value| options["consumer_commit"] = value }
|
|
opts.on("--status STATUS") { |value| options["status"] = value }
|
|
opts.on("--certified-at TIMESTAMP") { |value| options["certified_at"] = value }
|
|
opts.on("--evidence PATH") { |value| options["evidence"] << value }
|
|
opts.on("--previous-status STATUS") { |value| options["previous_status"] = value }
|
|
opts.on("--previous-certified-at TIMESTAMP") { |value| options["previous_certified_at"] = value }
|
|
opts.on("--previous-evidence PATH") { |value| options["previous_evidence"] << value }
|
|
opts.on("--dry-run") { options["dry_run"] = true }
|
|
end
|
|
|
|
def required!(options, *keys)
|
|
missing = keys.reject { |key| options[key] && (!options[key].respond_to?(:empty?) || !options[key].empty?) }
|
|
raise OptionParser::MissingArgument, missing.join(", ") unless missing.empty?
|
|
end
|
|
|
|
begin
|
|
parser.parse!(ARGV)
|
|
raise OptionParser::InvalidArgument, "unexpected arguments: #{ARGV.join(' ')}" unless ARGV.empty?
|
|
|
|
promoter = OpenCodeCompat::RuntimeTuplePromoter.new(root: root)
|
|
case command
|
|
when "fingerprint"
|
|
required!(options, "consumer", "consumer_commit")
|
|
puts JSON.pretty_generate(
|
|
promoter.fingerprints(
|
|
consumer: options.fetch("consumer"),
|
|
consumer_commit: options.fetch("consumer_commit")
|
|
)
|
|
)
|
|
when "promote"
|
|
required!(options, "consumer", "consumer_commit", "status", "certified_at", "evidence")
|
|
previous_fields = %w[previous_status previous_certified_at previous_evidence]
|
|
supplied_previous_fields = previous_fields.select do |key|
|
|
value = options[key]
|
|
value && (!value.respond_to?(:empty?) || !value.empty?)
|
|
end
|
|
if supplied_previous_fields.any? && supplied_previous_fields.length != previous_fields.length
|
|
raise OptionParser::MissingArgument, (previous_fields - supplied_previous_fields).join(", ")
|
|
end
|
|
|
|
previous_certification = if supplied_previous_fields.empty?
|
|
nil
|
|
else
|
|
{
|
|
"status" => options.fetch("previous_status"),
|
|
"certified_at" => options.fetch("previous_certified_at"),
|
|
"evidence" => options.fetch("previous_evidence")
|
|
}
|
|
end
|
|
promoted = promoter.promote(
|
|
consumer: options.fetch("consumer"),
|
|
consumer_commit: options.fetch("consumer_commit"),
|
|
certification: {
|
|
"status" => options.fetch("status"),
|
|
"certified_at" => options.fetch("certified_at"),
|
|
"evidence" => options.fetch("evidence")
|
|
},
|
|
previous_certification: previous_certification,
|
|
dry_run: options.fetch("dry_run", false)
|
|
)
|
|
if options.fetch("dry_run", false)
|
|
puts JSON.pretty_generate(promoted)
|
|
else
|
|
current = promoted.fetch("consumers").fetch(options.fetch("consumer")).fetch("current")
|
|
puts JSON.pretty_generate(
|
|
"consumer" => options.fetch("consumer"),
|
|
"current_consumer_commit" => current.fetch("consumer_commit"),
|
|
"current_tuple_sha256" => current.dig("certification", "tuple_sha256"),
|
|
"migration_state" => promoted.fetch("migration_state")
|
|
)
|
|
end
|
|
else
|
|
raise OptionParser::InvalidArgument, "command must be fingerprint or promote"
|
|
end
|
|
rescue OpenCodeCompat::PromotionError, OptionParser::ParseError => e
|
|
warn "error: #{e.message}"
|
|
warn parser
|
|
exit 1
|
|
end
|