Compare commits

...

13 commits

Author SHA1 Message Date
dependabot[bot]
39c377de17
Merge be7533da09 into fa93870a99 2026-05-19 23:22:39 +00:00
Adam Moussa
fa93870a99 Fix RN bundle phase: align metro-config and babel-preset with RN 0.85
The "Bundle React Native code and images" Xcode build phase failed
because @react-native/metro-config and @react-native/babel-preset were
pinned to 0.79.x while react-native is 0.85.3. The 0.79 metro-config
API is incompatible with the 0.85 bundler script.

Also fixes xcodebuild_formatter from "" to "cat" to avoid empty pipe.
2026-05-19 19:21:33 -04:00
Adam Moussa
ddb1e52e68 Fix .xcode.env.local path: Fastlane runs from fastlane/ not mobile/ 2026-05-19 18:57:05 -04:00
Adam Moussa
8b997b9086 Fix NODE_BINARY for Xcode build phases in CI
Write .xcode.env.local with explicit node path so the "Bundle React
Native code and images" build phase can find node. Xcode build phases
don't inherit the GitHub Actions PATH. Also disable xcbeautify for
this run to see raw xcodebuild output for debugging.
2026-05-19 18:54:42 -04:00
Adam Moussa
0326909e51 Add verbose match output and keychain diagnostics for signing issue
Certificate installs to keychain but security find-identity shows
no signing identities. Added verbose match, explicit keychain params,
setup_ci force, profile_name in update_code_signing_settings, and
diagnostic security find-identity commands to diagnose the import.
2026-05-19 18:47:09 -04:00
Adam Moussa
69f582f0b3 Configure manual code signing for CI builds
Add update_code_signing_settings to disable automatic signing and
set development team (9KAQYC653W) + Apple Distribution identity.
Fixes Xcode error "Signing requires a development team" in CI.
2026-05-19 18:43:53 -04:00
Adam Moussa
a1f2e22562 Add headerless-body and DER-wrap strategies to ASC key normalizer
The .p8 file from Apple has no PEM headers, just the raw base64
body. Add strategies for: headerless body wrapped with PEM headers,
base64-decoded DER re-wrapped as PEM, and double-decoded DER. Also
show hex bytes in diagnostics for better binary data analysis.
Secret has been re-set with properly PEM-wrapped + base64-encoded
content matching the reusable workflow's expected format.
2026-05-19 18:40:29 -04:00
Adam Moussa
279cd6c94a 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.
2026-05-19 18:32:46 -04:00
Adam Moussa
9cd12ceb79 Fix ASC key format detection: handle both raw PEM and base64
The secret may contain either raw PEM text (with BEGIN header) or
base64-encoded PEM. Detect format and pass appropriately to fastlane
instead of blindly base64-decoding (which corrupts raw PEM content).
2026-05-18 19:30:56 -04:00
Adam Moussa
a0ccf676b8 Use prepend to fix OpenSSL::PKey::EC.new on OpenSSL 3.x
alias_method doesn't reliably wrap C-extension class methods. Switch to
singleton_class.prepend which correctly intercepts the call chain. Falls
back to OpenSSL::PKey.read when EC.new raises on PKCS#8 format keys.
2026-05-18 19:27:26 -04:00
Adam Moussa
e355809b58 Fix OpenSSL 3.x EC key parsing in Fastfile
Fastlane 2.234.0 uses OpenSSL::PKey::EC.new which fails with "invalid
curve name" on PKCS#8 keys under OpenSSL 3.x. Add monkey-patch to fall
back to OpenSSL::PKey.read which handles both formats.
2026-05-18 19:22:46 -04:00
Adam Moussa
efa77c6ce7 Fix ASC key parsing: decode base64 before passing to Fastlane
Some checks are pending
Deploy / Deploy to AWS (push) Waiting to run
The app_store_connect_api_key action fails with "invalid curve name"
when is_key_content_base64 is true on macOS runners with OpenSSL 3.x.
Decoding the key manually and passing the raw PEM content avoids the
OpenSSL incompatibility. Also reverts the Fastlane version pin since
2.235.0 doesn't exist.
2026-05-18 18:52:15 -04:00
Adam Moussa
a4c78048ed Bump Fastlane >= 2.235.0 to fix OpenSSL curve name error
Fastlane 2.234.0 fails with "invalid curve name" on macos-latest
runners due to an OpenSSL 3.x incompatibility in the ASC API key
parsing. Fixed in 2.235.0. Removed lockfile so CI regenerates it
with the correct Ruby/bundler version.
2026-05-18 18:49:28 -04:00
4 changed files with 348 additions and 1163 deletions

1
.gitignore vendored
View file

@ -44,6 +44,7 @@ TestResults/
# React Native / Mobile
mobile/ios/Pods/
mobile/ios/build/
mobile/ios/.xcode.env.local
mobile/android/.gradle/
mobile/android/app/build/
mobile/android/build/

View file

@ -1,18 +1,137 @@
require 'openssl'
require 'base64'
require 'tempfile'
# 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
rescue OpenSSL::PKey::ECError
raise if arg.nil? || !arg.is_a?(String)
OpenSSL::PKey.read(arg)
end
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
unless with_newlines.include?("BEGIN")
body = cleaned.strip.gsub(/\s+/, '')
pem = "-----BEGIN PRIVATE KEY-----\n#{body.scan(/.{1,64}/).join("\n")}\n-----END PRIVATE KEY-----\n"
candidates << ["headerless body → PEM", pem]
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]
der_pem = "-----BEGIN PRIVATE KEY-----\n#{Base64.strict_encode64(decoded).scan(/.{1,64}/).join("\n")}\n-----END PRIVATE KEY-----\n"
candidates << ["base64→DER→PEM", der_pem]
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", "")]
elsif double.length.between?(32, 256)
der_pem = "-----BEGIN PRIVATE KEY-----\n#{Base64.strict_encode64(double).scan(/.{1,64}/).join("\n")}\n-----END PRIVATE KEY-----\n"
candidates << ["double-base64→DER→PEM", der_pem]
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)
hex = d.bytes[0..15].map { |b| format('%02x', b) }.join(' ')
UI.error("After base64 decode — length: #{d.bytesize}, first 16 bytes hex: #{hex}")
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
desc "Build and upload to TestFlight"
lane :beta do
setup_ci
setup_ci(force: true)
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: ENV["ASC_KEY_CONTENT"],
is_key_content_base64: true
key_filepath: key_path
)
match(type: "appstore", readonly: true)
File.delete(key_path) if File.exist?(key_path)
match(
type: "appstore",
readonly: true,
keychain_name: "fastlane_tmp_keychain",
keychain_password: ""
)
update_code_signing_settings(
use_automatic_signing: false,
team_id: "9KAQYC653W",
code_sign_identity: "Apple Distribution",
profile_name: "match AppStore com.seahavenind.proposals",
path: "ios/ProposalSystem.xcodeproj"
)
node_path = sh("which node").strip
xcode_env_path = File.join(__dir__, "..", "ios", ".xcode.env.local")
File.write(xcode_env_path, "export NODE_BINARY=#{node_path}\n")
UI.message("NODE_BINARY set to #{node_path} at #{xcode_env_path}")
increment_build_number(
build_number: ENV["GITHUB_RUN_NUMBER"],
@ -24,8 +143,10 @@ platform :ios do
scheme: "ProposalSystem",
configuration: "Release",
export_method: "app-store",
export_team_id: "9KAQYC653W",
output_directory: "build",
output_name: "ProposalSystem.ipa"
output_name: "ProposalSystem.ipa",
xcodebuild_formatter: "cat"
)
upload_to_testflight(skip_waiting_for_build_processing: true)

1375
mobile/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -35,8 +35,8 @@
"devDependencies": {
"@react-native-community/cli": "^20.1.3",
"@react-native-community/cli-platform-ios": "^20.1.3",
"@react-native/babel-preset": "^0.79.0",
"@react-native/metro-config": "^0.79.0",
"@react-native/babel-preset": "^0.85.3",
"@react-native/metro-config": "^0.85.3",
"@types/react": "^19.0.0",
"@types/react-native-vector-icons": "^6.4.18",
"typescript": "~5.7.0"