Files
opencode-compat/scripts/promote_runtime_tuple.rb
Ajay Krishnan 3bb0877034
Some checks failed
Candidate compatibility / prepare (push) Successful in 12s
Candidate compatibility / repository (push) Successful in 17s
Candidate compatibility / image ${{ matrix.id }} (push) Has been skipped
Candidate compatibility / fixture-contract (push) Successful in 48s
Candidate compatibility / lockstep clients Ruby 4.0 (push) Successful in 1m0s
Candidate compatibility / lockstep clients Ruby 3.4 (push) Successful in 1m1s
Candidate compatibility / lockstep clients Ruby 3.2 (push) Successful in 1m4s
Candidate compatibility / lockstep clients Ruby 3.3 (push) Successful in 1m43s
Candidate compatibility / Gitea full exact image matrix (push) Successful in 1m48s
Watch upstream OpenCode / open-compatibility-pr (push) Failing after 12s
Certify alpha8 production and rollback tuples (#7)
* Add explicit rollback bootstrap and maintenance model

* Certify alpha8 production and rollback tuples
2026-07-20 01:33:27 -07:00

146 lines
5.9 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]
ruby scripts/promote_runtime_tuple.rb bootstrap-current --consumer NAME --consumer-commit SHA \\
--status pass --certified-at TIMESTAMP --evidence evidence/FILE.json \\
--acknowledge-degraded-rollback \\
accept-degraded-rollback-with-failed-emergency-provenance [--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("--acknowledge-degraded-rollback VALUE") do |value|
options["degraded_rollback_acknowledgement"] = value
end
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
when "bootstrap-current"
required!(
options,
"consumer",
"consumer_commit",
"status",
"certified_at",
"evidence",
"degraded_rollback_acknowledgement"
)
bootstrapped = promoter.bootstrap_current(
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")
},
acknowledgement: options.fetch("degraded_rollback_acknowledgement"),
dry_run: options.fetch("dry_run", false)
)
if options.fetch("dry_run", false)
puts JSON.pretty_generate(bootstrapped)
else
consumer = bootstrapped.fetch("consumers").fetch(options.fetch("consumer"))
puts JSON.pretty_generate(
"consumer" => options.fetch("consumer"),
"current_consumer_commit" => consumer.dig("current", "consumer_commit"),
"current_tuple_sha256" => consumer.dig("current", "certification", "tuple_sha256"),
"previous" => consumer.fetch("previous"),
"rollback_state" => consumer.fetch("rollback_state"),
"migration_state" => bootstrapped.fetch("migration_state")
)
end
else
raise OptionParser::InvalidArgument, "command must be fingerprint, promote, or bootstrap-current"
end
rescue OpenCodeCompat::PromotionError, OptionParser::ParseError => e
warn "error: #{e.message}"
warn parser
exit 1
end