Submit store deliveries for review

This commit is contained in:
tianma-if committed 2026-07-29 20:39:15 +08:00
1 parent c941dcf032
commit d133d1a7fe
12 files changed
+458 -128

No files matched your search

+30 -12
View File
@@ -17,20 +17,15 @@ on:
- android
- ios
android_track:
description: "Google Play track (production is uploaded as a Draft)"
description: "Google Play track"
required: true
default: internal
default: production
type: choice
options:
- internal
- production
- alpha
- beta
- production
production_confirmation:
description: "For Google Play production, type the exact release tag"
required: false
default: ""
type: string
- internal
permissions:
contents: read
@@ -96,15 +91,13 @@ jobs:
ANDROID_TRACK: ${{ inputs.android_track }}
PLATFORM: ${{ inputs.platform }}
PREVIOUS_TAG: ${{ steps.release.outputs.previous_tag }}
PRODUCTION_CONFIRMATION: ${{ inputs.production_confirmation }}
RELEASE_TAG: ${{ inputs.release_tag }}
run: |
node scripts/validate-store-delivery.mjs \
"$RELEASE_TAG" \
"$PREVIOUS_TAG" \
"$PLATFORM" \
"$ANDROID_TRACK" \
"$PRODUCTION_CONFIRMATION" |
"$ANDROID_TRACK" |
tee -a "$GITHUB_OUTPUT"
android:
@@ -192,6 +185,13 @@ jobs:
with:
bun-version: 1.3.14
- name: Set up Ruby and fastlane
uses: ruby/setup-ruby@v1
with:
ruby-version: "3.3"
bundler-cache: true
working-directory: apps/mobile
- name: Install dependencies
run: bun install --frozen-lockfile
@@ -213,9 +213,13 @@ jobs:
--json
)"
build_id="$(jq -r 'if type == "array" then .[0].id else .id end' <<<"$build_json")"
build_number="$(jq -r 'if type == "array" then .[0].appBuildVersion else .appBuildVersion end' <<<"$build_json")"
test -n "$build_id"
test "$build_id" != "null"
test -n "$build_number"
test "$build_number" != "null"
echo "build_id=$build_id" >> "$GITHUB_OUTPUT"
echo "build_number=$build_number" >> "$GITHUB_OUTPUT"
working-directory: apps/mobile
- name: Upload archive to App Store Connect and TestFlight
@@ -229,3 +233,17 @@ jobs:
--non-interactive \
--wait
working-directory: apps/mobile
- name: Submit the uploaded build to App Review
env:
APP_STORE_BUILD_NUMBER: ${{ steps.build.outputs.build_number }}
APP_STORE_CONNECT_API_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_API_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY_ID: ${{ secrets.APP_STORE_CONNECT_API_KEY_ID }}
APP_STORE_CONNECT_API_KEY_P8_BASE64: ${{ secrets.APP_STORE_CONNECT_API_KEY_P8_BASE64 }}
APP_STORE_VERSION: ${{ needs.plan.outputs.version }}
run: |
test -n "$APP_STORE_CONNECT_API_ISSUER_ID"
test -n "$APP_STORE_CONNECT_API_KEY_ID"
test -n "$APP_STORE_CONNECT_API_KEY_P8_BASE64"
bundle exec fastlane ios submit_review
working-directory: apps/mobile
+3
View File
@@ -0,0 +1,3 @@
source "https://rubygems.org"
gem "fastlane", "2.237.0"
+344
View File
@@ -0,0 +1,344 @@
GEM
remote: https://rubygems.org/
specs:
CFPropertyList (3.0.8)
abbrev (0.1.2)
addressable (2.9.0)
public_suffix (>= 2.0.2, < 8.0)
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.4.0)
aws-partitions (1.1274.0)
aws-sdk-core (3.254.0)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
bigdecimal
jmespath (~> 1, >= 1.6.1)
logger
aws-sdk-kms (1.130.0)
aws-sdk-core (~> 3, >= 3.254.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.228.1)
aws-sdk-core (~> 3, >= 3.254.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
aws-sigv4 (1.12.1)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.3.0)
benchmark (0.5.0)
bigdecimal (4.1.2)
claide (1.1.0)
colored (1.2)
colored2 (3.1.2)
commander (4.6.0)
highline (~> 2.0.0)
csv (3.3.6)
declarative (0.0.20)
digest-crc (0.7.0)
rake (>= 12.0.0, < 14.0.0)
domain_name (0.6.20240107)
dotenv (2.8.1)
emoji_regex (3.2.3)
excon (1.6.0)
logger
faraday (1.10.6)
faraday-em_http (~> 1.0)
faraday-em_synchrony (~> 1.0)
faraday-excon (~> 1.1)
faraday-httpclient (~> 1.0)
faraday-multipart (~> 1.0)
faraday-net_http (~> 1.0)
faraday-net_http_persistent (~> 1.0)
faraday-patron (~> 1.0)
faraday-rack (~> 1.0)
faraday-retry (~> 1.0)
ruby2_keywords (>= 0.0.4)
faraday-cookie_jar (0.0.8)
faraday (>= 0.8.0)
http-cookie (>= 1.0.0)
faraday-em_http (1.0.0)
faraday-em_synchrony (1.0.1)
faraday-excon (1.1.0)
faraday-httpclient (1.0.1)
faraday-multipart (1.2.0)
multipart-post (~> 2.0)
faraday-net_http (1.0.2)
faraday-net_http_persistent (1.2.0)
faraday-patron (1.0.0)
faraday-rack (1.0.0)
faraday-retry (1.0.4)
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.4.1)
fastlane (2.237.0)
CFPropertyList (>= 2.3, < 5.0.0)
abbrev (~> 0.1)
addressable (>= 2.9.0, < 3.0.0)
artifactory (~> 3.0)
aws-sdk-s3 (~> 1.197)
babosa (>= 1.0.3, < 2.0.0)
base64 (~> 0.2)
benchmark (>= 0.1.0)
bundler (>= 2.4.0, < 5.0.0)
colored (~> 1.2)
commander (~> 4.6)
csv (~> 3.3)
dotenv (>= 2.1.1, < 3.0.0)
emoji_regex (>= 0.1, < 4.0)
excon (>= 0.71.0, < 2.0.0)
faraday (~> 1.0)
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
fastlane-sirp (>= 1.1.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
google-cloud-env (>= 1.6.0, < 2.3.0)
google-cloud-storage (~> 1.31)
highline (~> 2.0)
http-cookie (~> 1.0.5)
json (< 3.0.0)
jwt (>= 2.10.3, < 4)
logger (>= 1.6, < 2.0)
mini_magick (>= 4.9.4, < 5.0.0)
multi_json (~> 1.12)
multipart-post (>= 2.0.0, < 3.0.0)
mutex_m (~> 0.3)
naturally (~> 2.2)
nkf (~> 0.2)
optparse (>= 0.1.1, < 1.0.0)
ostruct (>= 0.1.0)
plist (>= 3.1.0, < 4.0.0)
rubyzip (>= 2.0.0, < 3.0.0)
security (= 0.1.5)
simctl (~> 1.6.3)
terminal-notifier (>= 2.0.0, < 3.0.0)
terminal-table (~> 3)
tty-screen (>= 0.6.3, < 1.0.0)
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.4.1)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-sirp (1.1.0)
gh_inspector (1.1.3)
google-apis-androidpublisher_v3 (0.106.0)
google-apis-core (>= 0.15.0, < 2.a)
google-apis-core (0.18.0)
addressable (~> 2.5, >= 2.5.1)
googleauth (~> 1.9)
httpclient (>= 2.8.3, < 3.a)
mini_mime (~> 1.0)
mutex_m
representable (~> 3.0)
retriable (>= 2.0, < 4.a)
google-apis-iamcredentials_v1 (0.28.0)
google-apis-core (>= 0.15.0, < 2.a)
google-apis-playcustomapp_v1 (0.18.0)
google-apis-core (>= 0.15.0, < 2.a)
google-apis-storage_v1 (0.65.0)
google-apis-core (>= 0.15.0, < 2.a)
google-cloud-core (1.9.0)
google-cloud-env (>= 1.0, < 3.a)
google-cloud-errors (~> 1.0)
google-cloud-env (2.2.2)
base64 (~> 0.2)
faraday (>= 1.0, < 3.a)
google-cloud-errors (1.7.0)
google-cloud-storage (1.62.0)
addressable (~> 2.8)
digest-crc (~> 0.4)
google-apis-core (>= 0.18, < 2)
google-apis-iamcredentials_v1 (~> 0.18)
google-apis-storage_v1 (>= 0.42)
google-cloud-core (~> 1.6)
googleauth (~> 1.9)
mini_mime (~> 1.0)
google-logging-utils (0.2.0)
googleauth (1.17.3)
faraday (>= 1.0, < 3.a)
google-cloud-env (~> 2.2)
google-logging-utils (~> 0.1)
jwt (>= 1.4, < 4.0)
os (>= 0.9, < 2.0)
pstore (~> 0.1)
signet (>= 0.16, < 2.a)
highline (2.0.3)
http-cookie (1.0.8)
domain_name (~> 0.5)
httpclient (2.9.0)
mutex_m
jmespath (1.6.2)
json (2.21.1)
jwt (3.2.0)
base64
logger (1.7.0)
mini_magick (4.13.2)
mini_mime (1.1.5)
multi_json (1.21.1)
multipart-post (2.4.1)
mutex_m (0.3.0)
nanaimo (0.4.0)
naturally (2.3.0)
nkf (0.3.0)
optparse (0.8.1)
os (1.1.4)
ostruct (0.6.3)
plist (3.7.2)
pstore (0.2.1)
public_suffix (7.0.5)
rake (13.4.2)
representable (3.2.0)
declarative (< 0.1.0)
trailblazer-option (>= 0.1.1, < 0.2.0)
uber (< 0.2.0)
retriable (3.8.0)
rexml (3.4.4)
rouge (3.28.0)
ruby2_keywords (0.0.5)
rubyzip (2.4.1)
security (0.1.5)
signet (0.22.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 4.0)
simctl (1.6.10)
CFPropertyList
naturally
terminal-notifier (2.0.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
trailblazer-option (0.1.2)
tty-cursor (0.7.1)
tty-screen (0.8.2)
tty-spinner (0.9.3)
tty-cursor (~> 0.7)
uber (0.1.0)
unicode-display_width (2.6.0)
word_wrap (1.0.0)
xcodeproj (1.28.1)
CFPropertyList (>= 2.3.3, < 4.0)
atomos (~> 0.1.3)
base64
claide (>= 1.0.2, < 2.0)
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
nkf
rexml (>= 3.3.6, < 4.0)
xcpretty (0.4.1)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
PLATFORMS
arm64-darwin-25
ruby
DEPENDENCIES
fastlane (= 2.237.0)
CHECKSUMS
CFPropertyList (3.0.8) sha256=2c99d0d980536d3d7ab252f7bd59ac8be50fbdd1ff487c98c949bb66bb114261
abbrev (0.1.2) sha256=ad1b4eaaaed4cb722d5684d63949e4bde1d34f2a95e20db93aecfe7cbac74242
addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af
artifactory (3.0.17) sha256=3023d5c964c31674090d655a516f38ca75665c15084140c08b7f2841131af263
atomos (0.1.3) sha256=7d43b22f2454a36bace5532d30785b06de3711399cb1c6bf932573eda536789f
aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b
aws-partitions (1.1274.0) sha256=6e555daee7f561541ef7871036e4e943afeabc2eab87186161e5ed08eb083c75
aws-sdk-core (3.254.0) sha256=ee3e3220b8468a3c9e59daba18e6ec897bf5c7ce8adcc0670cfa2f1f092112fe
aws-sdk-kms (1.130.0) sha256=a2e83662ca31b77a2a19c9aa2f40a98165a67270c18c718fe1c70d0cbd7cd749
aws-sdk-s3 (1.228.1) sha256=8865f6c6d068a9713a2aa6fe8107d11b0020a93cd5110b3994526bd9560f553e
aws-sigv4 (1.12.1) sha256=6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00
babosa (1.0.4) sha256=18dea450f595462ed7cb80595abd76b2e535db8c91b350f6c4b3d73986c5bc99
base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c
bigdecimal (4.1.2) sha256=53d217666027eab4280346fba98e7d5b66baaae1b9c3c1c0ffe89d48188a3fbd
claide (1.1.0) sha256=6d3c5c089dde904d96aa30e73306d0d4bd444b1accb9b3125ce14a3c0183f82e
colored (1.2) sha256=9d82b47ac589ce7f6cab64b1f194a2009e9fd00c326a5357321f44afab2c1d2c
colored2 (3.1.2) sha256=b13c2bd7eeae2cf7356a62501d398e72fde78780bd26aec6a979578293c28b4a
commander (4.6.0) sha256=7d1ddc3fccae60cc906b4131b916107e2ef0108858f485fdda30610c0f2913d9
csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456
declarative (0.0.20) sha256=8021dd6cb17ab2b61233c56903d3f5a259c5cf43c80ff332d447d395b17d9ff9
digest-crc (0.7.0) sha256=64adc23a26a241044cbe6732477ca1b3c281d79e2240bcff275a37a5a0d78c07
domain_name (0.6.20240107) sha256=5f693b2215708476517479bf2b3802e49068ad82167bcd2286f899536a17d933
dotenv (2.8.1) sha256=c5944793349ae03c432e1780a2ca929d60b88c7d14d52d630db0508c3a8a17d8
emoji_regex (3.2.3) sha256=ecd8be856b7691406c6bf3bb3a5e55d6ed683ffab98b4aa531bb90e1ddcc564b
excon (1.6.0) sha256=ebe2ab83c055ef8e117c62c91a3535b966a59a64868188219d49ab90cd83b65f
faraday (1.10.6) sha256=7ff4802a6b312876a2241b3e641ce0d5045e168dd871b422c35b505e5261ad4d
faraday-cookie_jar (0.0.8) sha256=0140605823f8cc63c7028fccee486aaed8e54835c360cffc1f7c8c07c4299dbb
faraday-em_http (1.0.0) sha256=7a3d4c7079789121054f57e08cd4ef7e40ad1549b63101f38c7093a9d6c59689
faraday-em_synchrony (1.0.1) sha256=bf3ce45dcf543088d319ab051f80985ea6d294930635b7a0b966563179f81750
faraday-excon (1.1.0) sha256=b055c842376734d7f74350fe8611542ae2000c5387348d9ba9708109d6e40940
faraday-httpclient (1.0.1) sha256=4c8ff1f0973ff835be8d043ef16aaf54f47f25b7578f6d916deee8399a04d33b
faraday-multipart (1.2.0) sha256=7d89a949693714176f612323ca13746a2ded204031a6ba528adee788694ef757
faraday-net_http (1.0.2) sha256=63992efea42c925a20818cf3c0830947948541fdcf345842755510d266e4c682
faraday-net_http_persistent (1.2.0) sha256=0b0cbc8f03dab943c3e1cc58d8b7beb142d9df068b39c718cd83e39260348335
faraday-patron (1.0.0) sha256=dc2cd7b340bb3cc8e36bcb9e6e7eff43d134b6d526d5f3429c7a7680ddd38fa7
faraday-rack (1.0.0) sha256=ef60ec969a2bb95b8dbf24400155aee64a00fc8ba6c6a4d3968562bcc92328c0
faraday-retry (1.0.4) sha256=dc659233777fabf96c69c2ffe56c0a5d2c102af90321a42cc6c90157bcd716aa
faraday_middleware (1.2.1) sha256=d45b78c8ee864c4783fbc276f845243d4a7918a67301c052647bacabec0529e9
fastimage (2.4.1) sha256=c64bebd46b6fd8943ab70c1e6e85ff728f970f2e48f92ecd249b6bc3a540ad20
fastlane (2.237.0) sha256=bb1e867bc070fb328741b5e6e7606d5ba596941a9ecca0478f60ef22d1009db9
fastlane-sirp (1.1.0) sha256=10bc94f9682efd8e1badfb31452a76dd8981f1f3a33717c765fde6d75b54d847
gh_inspector (1.1.3) sha256=04cca7171b87164e053aa43147971d3b7f500fcb58177698886b48a9fc4a1939
google-apis-androidpublisher_v3 (0.106.0) sha256=b90b5312b70f8f731def88f24a2b8fb791fe1b979a7c2c14a905cb6cae19cf3f
google-apis-core (0.18.0) sha256=96b057816feeeab448139ed5b5c78eab7fc2a9d8958f0fbc8217dedffad054ee
google-apis-iamcredentials_v1 (0.28.0) sha256=0a92ffe6cc39c569554af2a77a25dfc61519ed8bbb64ab04cffdd352dc5ef106
google-apis-playcustomapp_v1 (0.18.0) sha256=44b277b9dee4a59ac5e9d98be1485edc5e382d2f9d73c79ae8908a455786a254
google-apis-storage_v1 (0.65.0) sha256=4247aa542931691f6988ab61e45cdea2878cbfe897a98eec4af9cdcb678eb359
google-cloud-core (1.9.0) sha256=ab55409f51488e8deefb6edcc1ce4771dfb5da2fe7b3bc075709a030c2b682a4
google-cloud-env (2.2.2) sha256=94bed40e05a67e9468ce1cb38389fba9a90aa8fc62fc9e173204c1dca59e21e7
google-cloud-errors (1.7.0) sha256=6e682f42d89aae08689f36f28495de629d756da076bafff0003bac7f8042ebb3
google-cloud-storage (1.62.0) sha256=e2c3c08bf8fd40d50be92304084942203314d4fc0ee52028e99f9359c3ad1330
google-logging-utils (0.2.0) sha256=675462b4ea5affa825a3442694ca2d75d0069455a1d0956127207498fca3df7b
googleauth (1.17.3) sha256=9a655455885c0c17a8b70537acd2a992a7c7842c43c06c6612dfb0d93e755d58
highline (2.0.3) sha256=2ddd5c127d4692721486f91737307236fe005352d12a4202e26c48614f719479
http-cookie (1.0.8) sha256=b14fe0445cf24bf9ae098633e9b8d42e4c07c3c1f700672b09fbfe32ffd41aa6
httpclient (2.9.0) sha256=4b645958e494b2f86c2f8a2f304c959baa273a310e77a2931ddb986d83e498c8
jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1
json (2.21.1) sha256=13a43df75d95641443f5702dff350f237164a9d811ff0f2c2800d4d980220583
jwt (3.2.0) sha256=5419b1fe37b1da0982bd07051f573a8b8789ab724c2aa7e785e4784a3ed217d7
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
mini_magick (4.13.2) sha256=71d6258e0e8a3d04a9a0a09784d5d857b403a198a51dd4f882510435eb95ddd9
mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
multi_json (1.21.1) sha256=e6126a31808e3b4d19f483c775ceac34df190dffa62adfb63a165ee14ba68080
multipart-post (2.4.1) sha256=9872d03a8e552020ca096adadbf5e3cb1cd1cdd6acd3c161136b8a5737cdb4a8
mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751
nanaimo (0.4.0) sha256=faf069551bab17f15169c1f74a1c73c220657e71b6e900919897a10d991d0723
naturally (2.3.0) sha256=459923cf76c2e6613048301742363200c3c7e4904c324097d54a67401e179e01
nkf (0.3.0) sha256=357a8dbeba38b727b75930f665146546076a394a1c243faf634ff176e3588895
optparse (0.8.1) sha256=42bea10d53907ccff4f080a69991441d611fbf8733b60ed1ce9ee365ce03bd1a
os (1.1.4) sha256=57816d6a334e7bd6aed048f4b0308226c5fb027433b67d90a9ab435f35108d3f
ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912
plist (3.7.2) sha256=d37a4527cc1116064393df4b40e1dbbc94c65fa9ca2eec52edf9a13616718a42
pstore (0.2.1) sha256=03904d0f2c66579e96d1e6704cdabc0c88df7ea8ed8782d9f3569f6f6c702c1a
public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623
rake (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701
representable (3.2.0) sha256=cc29bf7eebc31653586849371a43ffe36c60b54b0a6365b5f7d95ec34d1ebace
retriable (3.8.0) sha256=9f2f1b0207594c7817f17f671587b8ec7587387ac6cebda6c941a802bb98a8e5
rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142
rouge (3.28.0) sha256=0d6de482c7624000d92697772ab14e48dca35629f8ddf3f4b21c99183fd70e20
ruby2_keywords (0.0.5) sha256=ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef
rubyzip (2.4.1) sha256=8577c88edc1fde8935eb91064c5cb1aef9ad5494b940cf19c775ee833e075615
security (0.1.5) sha256=3a977a0eca7706e804c96db0dd9619e0a94969fe3aac9680fcfc2bf9b8a833b7
signet (0.22.0) sha256=b76d495ccb07ad35dbc89f3e920665a9d8ed717141955034005d7843dcfe4780
simctl (1.6.10) sha256=b99077f4d13ad81eace9f86bf5ba4df1b0b893a4d1b368bd3ed59b5b27f9236b
terminal-notifier (2.0.0) sha256=7a0d2b2212ab9835c07f4b2e22a94cff64149dba1eed203c04835f7991078cea
terminal-table (3.0.2) sha256=f951b6af5f3e00203fb290a669e0a85c5dd5b051b3b023392ccfd67ba5abae91
trailblazer-option (0.1.2) sha256=20e4f12ea4e1f718c8007e7944ca21a329eee4eed9e0fa5dde6e8ad8ac4344a3
tty-cursor (0.7.1) sha256=79534185e6a777888d88628b14b6a1fdf5154a603f285f80b1753e1908e0bf48
tty-screen (0.8.2) sha256=c090652115beae764336c28802d633f204fb84da93c6a968aa5d8e319e819b50
tty-spinner (0.9.3) sha256=0e036f047b4ffb61f2aa45f5a770ec00b4d04130531558a94bfc5b192b570542
uber (0.1.0) sha256=5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc
unicode-display_width (2.6.0) sha256=12279874bba6d5e4d2728cef814b19197dbb10d7a7837a869bab65da943b7f5a
word_wrap (1.0.0) sha256=f556d4224c812e371000f12a6ee8102e0daa724a314c3f246afaad76d82accc7
xcodeproj (1.28.1) sha256=6f12670f00739d9817ca27ac89d6ef01cc86050e22a0bc08a3131487e5b5cddc
xcpretty (0.4.1) sha256=b14c50e721f6589ee3d6f5353e2c2cfcd8541fa1ea16d6c602807dd7327f3892
xcpretty-travis-formatter (1.0.1) sha256=aacc332f17cb7b2cba222994e2adc74223db88724fe76341483ad3098e232f93
BUNDLED WITH
4.0.16
+1 -2
View File
@@ -47,8 +47,7 @@
"store-production": {
"android": {
"track": "production",
"releaseStatus": "draft",
"changesNotSentForReview": true
"releaseStatus": "completed"
}
},
"store-ios": {
+33
View File
@@ -0,0 +1,33 @@
opt_out_usage
default_platform(:ios)
platform :ios do
desc "Submit an uploaded EAS build to App Review and release it automatically after approval"
lane :submit_review do
version = ENV.fetch("APP_STORE_VERSION")
build_number = ENV.fetch("APP_STORE_BUILD_NUMBER")
api_key = app_store_connect_api_key(
key_id: ENV.fetch("APP_STORE_CONNECT_API_KEY_ID"),
issuer_id: ENV.fetch("APP_STORE_CONNECT_API_ISSUER_ID"),
key_content: ENV.fetch("APP_STORE_CONNECT_API_KEY_P8_BASE64"),
is_key_content_base64: true,
in_house: false
)
deliver(
api_key: api_key,
app_identifier: "org.edgeever.mobile",
app_version: version,
build_number: build_number,
submit_for_review: true,
automatic_release: true,
phased_release: false,
force: true,
skip_binary_upload: true,
skip_metadata: true,
skip_screenshots: true
)
end
end
+5 -3
View File
@@ -115,9 +115,11 @@ bunx eas-cli build --platform ios --profile production
The first command requires the Apple Account Holder to authenticate and may
prompt for two-factor authentication. The production profile automatically
increments the App Store build number. Routine delivery should use the separate
store-delivery workflow, which builds from an immutable formal Release tag and
uploads to App Store Connect/TestFlight. Apple credentials, App Store Connect
API keys, certificates, and provisioning profiles must never be committed.
store-delivery workflow, which builds from an immutable formal Release tag,
uploads to App Store Connect, and submits that exact build to App Review.
Approved builds are released automatically. Apple credentials, App Store
Connect API keys, certificates, and provisioning profiles must never be
committed.
## EAS
+19 -24
View File
@@ -6,8 +6,9 @@ GitHub Releases and mobile store delivery are separate operations:
Google Play or App Store Connect.
- `bun run publish:stores` dispatches a manual store-delivery workflow for one
existing formal Release tag.
- Store publication remains a separate decision. Google Play production uploads
remain Drafts, and iOS uploads stop at App Store Connect/TestFlight.
- Store delivery is the authorization to submit. By default, Google Play uses
the Production track, while iOS continues from App Store Connect upload into
App Review. Approved builds are released automatically.
## Safety Model
@@ -19,8 +20,7 @@ store build starts, it verifies that:
- the audited range since the previous formal Release contains mobile runtime
changes;
- root and mobile app versions both match the Release tag;
- Android `versionCode` increased;
- a Google Play production request repeats the exact Release tag as confirmation.
- Android `versionCode` increased.
A Release that reused the previous mobile binary is intentionally rejected. It
does not represent a new store binary and should not be uploaded again.
@@ -34,6 +34,9 @@ Configure these GitHub repository secrets:
- `ANDROID_KEYSTORE_PASSWORD`
- `ANDROID_KEY_ALIAS`
- `ANDROID_KEY_PASSWORD`
- `APP_STORE_CONNECT_API_KEY_ID`
- `APP_STORE_CONNECT_API_ISSUER_ID`
- `APP_STORE_CONNECT_API_KEY_P8_BASE64`
Upload the Google Play service-account key to the Android application's EAS
Submit credentials. Configure the iOS distribution credentials and App Store
@@ -42,9 +45,8 @@ repository.
Create these GitHub Environments:
- `store-delivery` for Internal/Alpha/Beta and TestFlight delivery.
- `store-production` for Google Play production delivery. Configure required
reviewers on this environment.
- `store-delivery` for Android testing tracks and Apple App Review delivery.
- `store-production` for Google Play production delivery.
EAS Submit requires the application to exist in each store and Google Play API
submission requires the service account to have access to the application.
@@ -54,7 +56,7 @@ Follow the official
## Commands
Deliver both platforms to Google Play Internal and App Store Connect/TestFlight:
Submit both platforms to Google Play Production and Apple App Review:
```sh
bun run publish:stores -- --release v1.7.0
@@ -69,16 +71,6 @@ bun run publish:stores -- \
--android-track beta
```
Prepare a Google Play production Draft:
```sh
bun run publish:stores -- \
--release v1.7.0 \
--platform android \
--android-track production \
--confirm-production v1.7.0
```
Use `--dry-run` to print the GitHub workflow dispatch without starting it.
## Platform Behavior
@@ -89,15 +81,18 @@ The self-hosted release runner builds a signed AAB from the selected tag,
verifies its signature and R8 mapping, preserves both as GitHub Actions
artifacts, and uploads the AAB through EAS Submit.
Internal, Alpha, and Beta profiles use a completed release on the selected
testing track. Production uses `releaseStatus: draft` and
`changesNotSentForReview: true`; completing review and rollout remains a manual
Google Play Console action.
Internal, Alpha, Beta, and Production profiles use a completed release on the
selected track. The default command targets Production; use
`--android-track internal`, `alpha`, or `beta` only when a testing delivery is
explicitly requested.
### App Store Connect
EAS Build creates the signed iOS archive from the selected tag and increments
the remote iOS build number. EAS Submit uploads that exact build to App Store
Connect, where it becomes available to TestFlight after Apple finishes
processing it. The workflow does not select the build for an App Store version
or submit it for App Review.
processing it. Fastlane then selects the exact app version and build number,
submits it to App Review with an App Store Connect API key, and configures
automatic release after approval. Missing metadata, agreements, review
information, or credentials cause the workflow to fail without submitting a
different build.
+16 -22
View File
@@ -6,8 +6,9 @@ GitHub Release 与移动端商店交付是两个独立操作:
App Store Connect。
- `bun run publish:stores` 针对一个已经存在的正式 Release tag,触发手动商店
交付工作流。
- 商店正式发布仍然是独立决策。Google Play Production 上传会保持 Draft,
iOS 上传只到 App Store Connect/TestFlight。
- 触发商店交付就代表已经授权正式提交。默认情况下,Google Play 使用
Production 轨道;iOS 在上传 App Store Connect 后继续提交 App Review。审核
通过后自动发布。
## 安全模型
@@ -17,8 +18,7 @@ GitHub Release 与移动端商店交付是两个独立操作:
- Release 目标提交与 Git tag 指向同一个提交;
- 与上一个正式 Release 相比,审计范围内确实包含移动端运行时代码变化;
- 根版本和移动端 App 版本都与 Release tag 一致;
- Android `versionCode` 已递增;
- Google Play Production 请求再次输入了完全一致的 Release tag。
- Android `versionCode` 已递增。
如果某个 Release 复用了上一版移动端二进制,工作流会主动拒绝。它不代表新的
商店二进制,不应重复上传。
@@ -32,6 +32,9 @@ GitHub Release 与移动端商店交付是两个独立操作:
- `ANDROID_KEYSTORE_PASSWORD`
- `ANDROID_KEY_ALIAS`
- `ANDROID_KEY_PASSWORD`
- `APP_STORE_CONNECT_API_KEY_ID`
- `APP_STORE_CONNECT_API_ISSUER_ID`
- `APP_STORE_CONNECT_API_KEY_P8_BASE64`
将 Google Play 服务账号密钥上传到 Android 应用的 EAS Submit Credentials。
在 EAS 中配置 iOS 分发凭据和 App Store Connect API Key。凭据和私钥禁止提交
@@ -39,9 +42,8 @@ GitHub Release 与移动端商店交付是两个独立操作:
创建以下 GitHub Environments:
- `store-delivery`:用于 Internal/Alpha/Beta 和 TestFlight 交付。
- `store-production`:用于 Google Play Production 交付,并为该 Environment
配置 Required Reviewers。
- `store-delivery`:用于 Android 测试轨道和 Apple App Review 交付。
- `store-production`:用于 Google Play Production 交付。
EAS Submit 要求应用已经在对应商店中创建;Google Play API 提交还要求服务账号
拥有该应用的访问权限。配置方法参考官方
@@ -50,7 +52,7 @@ EAS Submit 要求应用已经在对应商店中创建;Google Play API 提交
## 命令
同时交付到 Google Play Internal 与 App Store Connect/TestFlight:
同时提交 Google Play Production 和 Apple App Review:
```sh
bun run publish:stores -- --release v1.7.0
@@ -65,16 +67,6 @@ bun run publish:stores -- \
--android-track beta
```
准备 Google Play Production Draft:
```sh
bun run publish:stores -- \
--release v1.7.0 \
--platform android \
--android-track production \
--confirm-production v1.7.0
```
使用 `--dry-run` 可以只输出将要触发的 GitHub 工作流,不实际启动。
## 各平台行为
@@ -84,12 +76,14 @@ bun run publish:stores -- \
自托管发布 Runner 会从指定 tag 构建签名 AAB,验证签名和 R8 Mapping,将两者
保留为 GitHub Actions Artifacts,然后通过 EAS Submit 上传 AAB。
Internal、Alpha 和 Beta 配置会在对应测试轨道创建 Completed Release。
Production 使用 `releaseStatus: draft` 和 `changesNotSentForReview: true`;
完成审核和发布仍需在 Google Play Console 中手动操作。
Internal、Alpha、Beta 和 Production 配置都会在所选轨道创建 Completed
Release。默认命令直接使用 Production;只有明确要求测试交付时才使用
`--android-track internal`、`alpha` 或 `beta`。
### App Store Connect
EAS Build 从指定 tag 创建签名 iOS Archive,并递增远端 iOS Build Number。
EAS Submit 将同一个构建上传到 App Store Connect;Apple 处理完成后,它会出现在
TestFlight 中。工作流不会为 App Store 版本选择构建,也不会自动提交 App Review。
TestFlight 中。随后 Fastlane 使用 App Store Connect API Key 精确选择相同的
App Version 和 Build Number,提交 App Review,并设置为审核通过后自动发布。
如果元数据、协议、审核信息或凭据不完整,工作流会失败,不会改为提交其他构建。
+2 -16
View File
@@ -12,8 +12,7 @@ const usage = `Usage:
Options:
--release <tag> Required formal GitHub Release tag
--platform <target> android, ios, or both (default: both)
--android-track <track> internal, alpha, beta, or production (default: internal)
--confirm-production <tag> Required when the Android track is production
--android-track <track> production, alpha, beta, or internal (default: production)
--repository <owner/name> GitHub repository (default: ${DEFAULT_REPOSITORY})
--dry-run Print the workflow dispatch plan
--help Show this help
@@ -23,8 +22,7 @@ export const parseStoreDeliveryArgs = (argv) => {
const options = {
releaseTag: "",
platform: "both",
androidTrack: "internal",
productionConfirmation: "",
androidTrack: "production",
repository: DEFAULT_REPOSITORY,
dryRun: false,
help: false,
@@ -33,7 +31,6 @@ export const parseStoreDeliveryArgs = (argv) => {
["--release", "releaseTag"],
["--platform", "platform"],
["--android-track", "androidTrack"],
["--confirm-production", "productionConfirmation"],
["--repository", "repository"],
]);
@@ -73,15 +70,6 @@ export const parseStoreDeliveryArgs = (argv) => {
"--android-track must be internal, alpha, beta, or production.",
);
}
if (
options.androidTrack === "production" &&
options.platform !== "ios" &&
options.productionConfirmation !== options.releaseTag
) {
throw new Error(
`Production delivery requires --confirm-production ${options.releaseTag}.`,
);
}
if (!/^[^/\s]+\/[^/\s]+$/.test(options.repository)) {
throw new Error("--repository must use owner/name format.");
}
@@ -103,8 +91,6 @@ const run = (options) => {
`platform=${options.platform}`,
"-f",
`android_track=${options.androidTrack}`,
"-f",
`production_confirmation=${options.productionConfirmation}`,
];
if (options.dryRun) {
console.log(`gh ${args.join(" ")}`);
+2 -16
View File
@@ -2,14 +2,13 @@ import { describe, expect, test } from "bun:test";
import { parseStoreDeliveryArgs } from "./store-delivery.mjs";
describe("store delivery command", () => {
test("uses safe testing defaults", () => {
test("uses full production delivery defaults", () => {
expect(
parseStoreDeliveryArgs(["--release", "v1.7.0"]),
).toMatchObject({
releaseTag: "v1.7.0",
platform: "both",
androidTrack: "internal",
productionConfirmation: "",
androidTrack: "production",
});
});
@@ -29,17 +28,4 @@ describe("store delivery command", () => {
parseStoreDeliveryArgs(["--release", "latest"])
).toThrow("stable vX.Y.Z");
});
test("requires exact confirmation for Google Play production", () => {
expect(() =>
parseStoreDeliveryArgs([
"--release",
"v1.7.0",
"--platform",
"android",
"--android-track",
"production",
])
).toThrow("--confirm-production v1.7.0");
});
});
+2 -15
View File
@@ -17,7 +17,6 @@ export const validateStoreDelivery = ({
changedFiles,
platform,
androidTrack,
productionConfirmation,
}) => {
const version = STABLE_RELEASE_TAG.exec(releaseTag)?.[1];
if (!version) {
@@ -50,16 +49,6 @@ export const validateStoreDelivery = ({
`Android versionCode must increase beyond ${previousVersionCode}; received ${currentVersionCode}.`,
);
}
if (
platform !== "ios" &&
androidTrack === "production" &&
productionConfirmation !== releaseTag
) {
throw new Error(
`Production delivery requires --confirm-production ${releaseTag}.`,
);
}
return {
version,
versionCode: currentVersionCode,
@@ -74,12 +63,11 @@ const run = () => {
releaseTag,
previousTag,
platform = "both",
androidTrack = "internal",
productionConfirmation = "",
androidTrack = "production",
] = process.argv.slice(2);
if (!releaseTag || !previousTag) {
console.error(
"Usage: node scripts/validate-store-delivery.mjs <release-tag> <previous-tag> [platform] [android-track] [production-confirmation]",
"Usage: node scripts/validate-store-delivery.mjs <release-tag> <previous-tag> [platform] [android-track]",
);
process.exit(2);
}
@@ -105,7 +93,6 @@ const run = () => {
changedFiles,
platform,
androidTrack,
productionConfirmation,
});
process.stdout.write(`version=${result.version}\n`);
+1 -18
View File
@@ -9,8 +9,7 @@ const validInput = {
previousVersionCode: 56,
changedFiles: ["apps/mobile/src/screens/WorkspaceScreen.tsx"],
platform: "both",
androidTrack: "internal",
productionConfirmation: "",
androidTrack: "production",
};
describe("store delivery validation", () => {
@@ -42,20 +41,4 @@ describe("store delivery validation", () => {
validateStoreDelivery({ ...validInput, currentVersionCode: 56 })
).toThrow("versionCode must increase");
});
test("requires an exact confirmation for production delivery", () => {
expect(() =>
validateStoreDelivery({
...validInput,
androidTrack: "production",
})
).toThrow("--confirm-production v1.7.0");
expect(
validateStoreDelivery({
...validInput,
androidTrack: "production",
productionConfirmation: "v1.7.0",
}).version,
).toBe("1.7.0");
});
});