mirror of
https://github.com/Sea-Haven-Industries/proposal-system.git
synced 2026-09-30 06:33:13 +00:00
Robust ASC key normalization with diagnostics for TestFlight deploy
Replace fragile BEGIN/base64 branch with multi-strategy key parser that handles raw PEM, escaped newlines, base64-encoded PEM, mangled line wrapping, CR/LF issues, and double-encoding. Validates key with OpenSSL::PKey.read before passing to Fastlane via key_filepath (temp file) instead of key_content to bypass Fastlane's own parsing. Prints safe diagnostics (no key material) if all strategies fail.
This commit is contained in:
parent
9cd12ceb79
commit
279cd6c94a
1 changed files with 78 additions and 13 deletions
|
|
@ -1,8 +1,9 @@
|
|||
require 'openssl'
|
||||
require 'base64'
|
||||
require 'tempfile'
|
||||
|
||||
# Fastlane 2.234.0 uses OpenSSL::PKey::EC.new which rejects PKCS#8 keys on
|
||||
# OpenSSL 3.x ("invalid curve name"). Prepend a wrapper that falls back to
|
||||
# OpenSSL::PKey.read, which handles both PKCS#8 and SEC1 formats.
|
||||
# OpenSSL 3.x rejects PKCS#8 keys via EC.new ("invalid curve name").
|
||||
# Prepend a wrapper that falls back to PKey.read for both formats.
|
||||
module OpenSSLECNewFix
|
||||
def new(arg = nil, *rest)
|
||||
super
|
||||
|
|
@ -13,6 +14,73 @@ module OpenSSLECNewFix
|
|||
end
|
||||
OpenSSL::PKey::EC.singleton_class.prepend(OpenSSLECNewFix)
|
||||
|
||||
def normalize_p8_key(raw)
|
||||
candidates = []
|
||||
|
||||
cleaned = raw.gsub("\r", "")
|
||||
with_newlines = cleaned.gsub('\n', "\n")
|
||||
candidates << ["raw (newlines normalized)", with_newlines]
|
||||
|
||||
if with_newlines.include?("BEGIN")
|
||||
b64_body = with_newlines.gsub(/-----(?:BEGIN|END)[^-]+-----/, '').gsub(/\s+/, '')
|
||||
rewrapped = "-----BEGIN PRIVATE KEY-----\n#{b64_body.scan(/.{1,64}/).join("\n")}\n-----END PRIVATE KEY-----\n"
|
||||
candidates << ["rewrapped PEM", rewrapped]
|
||||
end
|
||||
|
||||
begin
|
||||
decoded = Base64.decode64(cleaned.strip)
|
||||
if decoded.include?("BEGIN")
|
||||
decoded_clean = decoded.gsub("\r", "").gsub('\n', "\n")
|
||||
candidates << ["base64→PEM", decoded_clean]
|
||||
b64_body = decoded_clean.gsub(/-----(?:BEGIN|END)[^-]+-----/, '').gsub(/\s+/, '')
|
||||
rewrapped = "-----BEGIN PRIVATE KEY-----\n#{b64_body.scan(/.{1,64}/).join("\n")}\n-----END PRIVATE KEY-----\n"
|
||||
candidates << ["base64→PEM rewrapped", rewrapped]
|
||||
elsif decoded.length.between?(32, 256)
|
||||
candidates << ["base64→DER", decoded]
|
||||
end
|
||||
rescue StandardError
|
||||
# not valid base64
|
||||
end
|
||||
|
||||
begin
|
||||
double = Base64.decode64(Base64.decode64(cleaned.strip).strip)
|
||||
if double.include?("BEGIN")
|
||||
candidates << ["double-base64→PEM", double.gsub("\r", "")]
|
||||
end
|
||||
rescue StandardError
|
||||
# not double-encoded
|
||||
end
|
||||
|
||||
candidates.each do |name, content|
|
||||
begin
|
||||
OpenSSL::PKey.read(content)
|
||||
UI.success("ASC key parsed with strategy: #{name}")
|
||||
return content
|
||||
rescue StandardError => e
|
||||
UI.message("Strategy '#{name}' failed: #{e.class} — #{e.message}")
|
||||
end
|
||||
end
|
||||
|
||||
UI.error("=== ASC KEY DIAGNOSTIC (no key material shown) ===")
|
||||
UI.error("Raw byte length: #{raw.bytesize}")
|
||||
UI.error("Starts with BEGIN: #{raw.strip.start_with?('-----BEGIN')}")
|
||||
UI.error("Ends with -----: #{raw.strip.end_with?('-----')}")
|
||||
UI.error("Has real newlines: #{raw.include?("\n")}")
|
||||
UI.error("Has literal backslash-n: #{raw.include?('\\n')}")
|
||||
UI.error("Has carriage returns: #{raw.include?("\r")}")
|
||||
UI.error("Printable ASCII ratio: #{(raw.count(' -~').to_f / raw.bytesize * 100).round(1)}%")
|
||||
UI.error("Header (first 27 chars): #{raw[0..26]}")
|
||||
begin
|
||||
d = Base64.decode64(raw.strip)
|
||||
UI.error("After base64 decode — length: #{d.bytesize}, header: #{d[0..26]}")
|
||||
rescue StandardError
|
||||
UI.error("base64 decode raised an exception")
|
||||
end
|
||||
UI.error("=== END DIAGNOSTIC ===")
|
||||
|
||||
raise "Could not parse ASC_KEY_CONTENT in any known format. See diagnostics above."
|
||||
end
|
||||
|
||||
default_platform(:ios)
|
||||
|
||||
platform :ios do
|
||||
|
|
@ -20,22 +88,19 @@ platform :ios do
|
|||
lane :beta do
|
||||
setup_ci
|
||||
|
||||
raw = ENV["ASC_KEY_CONTENT"]
|
||||
if raw.include?("BEGIN")
|
||||
key_content = raw.gsub('\n', "\n")
|
||||
is_base64 = false
|
||||
else
|
||||
key_content = raw
|
||||
is_base64 = true
|
||||
end
|
||||
key_pem = normalize_p8_key(ENV["ASC_KEY_CONTENT"])
|
||||
|
||||
key_path = File.join(Dir.tmpdir, "asc_api_key.p8")
|
||||
File.write(key_path, key_pem)
|
||||
|
||||
app_store_connect_api_key(
|
||||
key_id: ENV["ASC_KEY_ID"],
|
||||
issuer_id: ENV["ASC_ISSUER_ID"],
|
||||
key_content: key_content,
|
||||
is_key_content_base64: is_base64
|
||||
key_filepath: key_path
|
||||
)
|
||||
|
||||
File.delete(key_path) if File.exist?(key_path)
|
||||
|
||||
match(type: "appstore", readonly: true)
|
||||
|
||||
increment_build_number(
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue