fix(android): require Play signing identity for links
This commit is contained in:
parent
07cf978415
commit
8e74f8601e
|
|
@ -98,6 +98,11 @@ WNH_FIREBASE_GCM_SENDER_ID=
|
||||||
# production. Keep both empty until the matching signed APK/AAB is available.
|
# production. Keep both empty until the matching signed APK/AAB is available.
|
||||||
ANDROID_APP_LINKS_PACKAGE_NAME=
|
ANDROID_APP_LINKS_PACKAGE_NAME=
|
||||||
ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=
|
ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=
|
||||||
|
# Production-only evidence from Google Play Console. This must contain the
|
||||||
|
# Play App Signing certificate fingerprint(s), not the local upload key, and
|
||||||
|
# every value must also appear in ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS.
|
||||||
|
# Development and test leave this empty.
|
||||||
|
ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=
|
||||||
# Public identifier of the locally held Google Play upload key. The private
|
# Public identifier of the locally held Google Play upload key. The private
|
||||||
# keystore and its randomized password live outside the repository under
|
# keystore and its randomized password live outside the repository under
|
||||||
# ~/.config/who_need_help/android-release/.
|
# ~/.config/who_need_help/android-release/.
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,13 @@ separate app-signing key used for distributed APKs:
|
||||||
- <https://support.google.com/googleplay/android-developer/answer/9859152>
|
- <https://support.google.com/googleplay/android-developer/answer/9859152>
|
||||||
- <https://docs.docker.com/build/building/secrets/>
|
- <https://docs.docker.com/build/building/secrets/>
|
||||||
|
|
||||||
|
The production checkout therefore keeps the locally measured upload
|
||||||
|
certificate and the Play Console app-signing certificate as distinct evidence.
|
||||||
|
`ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS` publishes every active identity,
|
||||||
|
while `ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS` must contain the
|
||||||
|
Play-delivered identity as a verified subset. A release is not marked ready
|
||||||
|
from the upload certificate alone.
|
||||||
|
|
||||||
## Public development and staging builds
|
## Public development and staging builds
|
||||||
|
|
||||||
The installable `development` and `staging` build types use the explicit public
|
The installable `development` and `staging` build types use the explicit public
|
||||||
|
|
|
||||||
|
|
@ -160,15 +160,19 @@ separate upload material under
|
||||||
```dotenv
|
```dotenv
|
||||||
WNH_ANDROID_SIGNING_KEY_ALIAS=who-need-help-upload
|
WNH_ANDROID_SIGNING_KEY_ALIAS=who-need-help-upload
|
||||||
ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile
|
ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile
|
||||||
ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=UPLOAD_OR_PLAY_SHA256
|
ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=UPLOAD_SHA256,PLAY_APP_SIGNING_SHA256
|
||||||
|
ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=PLAY_APP_SIGNING_SHA256
|
||||||
```
|
```
|
||||||
|
|
||||||
Run `./scripts/android-release-build.sh` from that production release checkout.
|
Run `./scripts/android-release-build.sh` from that production release checkout.
|
||||||
It produces an APK, Play AAB, package report, signing report, and lint report.
|
It produces an APK, Play AAB, package report, signing report, and lint report.
|
||||||
After Play App Signing is enabled, add the Play signing certificate fingerprint
|
After Play App Signing is enabled, add the Play signing certificate fingerprint
|
||||||
to the comma-separated App Links value; the upload certificate alone does not
|
to the comma-separated App Links value; the upload certificate alone does not
|
||||||
describe Play-delivered APKs. The production environment validator accepts
|
describe Play-delivered APKs. Record the same Play fingerprint separately in
|
||||||
multiple SHA-256 fingerprints and rejects partial or malformed configuration.
|
`ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS`; release readiness verifies
|
||||||
|
that every Play identity is present in the public App Links list. The production
|
||||||
|
environment validator accepts multiple SHA-256 fingerprints and rejects partial,
|
||||||
|
malformed, upload-only, or inconsistent configuration.
|
||||||
|
|
||||||
### Release capability inputs
|
### Release capability inputs
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,20 @@ firebase_client_values_valid() {
|
||||||
-n "${application_id#"$prefix"}" ]]
|
-n "${application_id#"$prefix"}" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
valid_sha256_fingerprint_list() {
|
||||||
|
local fingerprint compact
|
||||||
|
local -a fingerprint_list
|
||||||
|
|
||||||
|
IFS=',' read -r -a fingerprint_list <<<"$1"
|
||||||
|
[[ ${#fingerprint_list[@]} -gt 0 ]] || return 1
|
||||||
|
|
||||||
|
for fingerprint in "${fingerprint_list[@]}"; do
|
||||||
|
compact=${fingerprint//:/}
|
||||||
|
compact=${compact//[[:space:]]/}
|
||||||
|
[[ "$compact" =~ ^[0-9A-Fa-f]{64}$ ]] || return 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
failures=0
|
failures=0
|
||||||
warnings=0
|
warnings=0
|
||||||
|
|
||||||
|
|
@ -248,17 +262,55 @@ case "$deployment_env" in
|
||||||
production) expected_android_package=org.whoneedhelp.mobile ;;
|
production) expected_android_package=org.whoneedhelp.mobile ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if all_empty ANDROID_APP_LINKS_PACKAGE_NAME ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS; then
|
if all_empty ANDROID_APP_LINKS_PACKAGE_NAME \
|
||||||
|
ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS \
|
||||||
|
ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS; then
|
||||||
missing "Android App Links" "package name and signing certificate fingerprint"
|
missing "Android App Links" "package name and signing certificate fingerprint"
|
||||||
elif all_set ANDROID_APP_LINKS_PACKAGE_NAME ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS; then
|
elif all_set ANDROID_APP_LINKS_PACKAGE_NAME ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS; then
|
||||||
if [[ -n "$expected_android_package" &&
|
if [[ -z "$expected_android_package" ||
|
||||||
"$(value ANDROID_APP_LINKS_PACKAGE_NAME)" == "$expected_android_package" ]]; then
|
"$(value ANDROID_APP_LINKS_PACKAGE_NAME)" != "$expected_android_package" ]]; then
|
||||||
ready "Android App Links" "package and signing fingerprints match this environment"
|
|
||||||
else
|
|
||||||
invalid "Android App Links" "package does not match DEPLOYMENT_ENV=$deployment_env"
|
invalid "Android App Links" "package does not match DEPLOYMENT_ENV=$deployment_env"
|
||||||
|
elif ! valid_sha256_fingerprint_list \
|
||||||
|
"$(value ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS)"; then
|
||||||
|
invalid "Android App Links" "published signing fingerprints are malformed"
|
||||||
|
elif [[ "$deployment_env" != production ]]; then
|
||||||
|
ready "Android App Links" "package and signing fingerprints match this environment"
|
||||||
|
elif ! is_set ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS; then
|
||||||
|
missing "Android App Links" "production requires the Play App Signing SHA-256 fingerprint"
|
||||||
|
elif ! valid_sha256_fingerprint_list \
|
||||||
|
"$(value ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS)"; then
|
||||||
|
invalid "Android App Links" "Play App Signing fingerprints are malformed"
|
||||||
|
else
|
||||||
|
published_fingerprints=$(value ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS)
|
||||||
|
play_fingerprints=$(value ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS)
|
||||||
|
all_play_fingerprints_published=true
|
||||||
|
IFS=',' read -r -a play_fingerprint_list <<<"$play_fingerprints"
|
||||||
|
IFS=',' read -r -a published_fingerprint_list <<<"$published_fingerprints"
|
||||||
|
for play_fingerprint in "${play_fingerprint_list[@]}"; do
|
||||||
|
compact_play=${play_fingerprint//:/}
|
||||||
|
compact_play=${compact_play//[[:space:]]/}
|
||||||
|
play_found=false
|
||||||
|
for published_fingerprint in "${published_fingerprint_list[@]}"; do
|
||||||
|
compact_published=${published_fingerprint//:/}
|
||||||
|
compact_published=${compact_published//[[:space:]]/}
|
||||||
|
if [[ "${compact_play^^}" == "${compact_published^^}" ]]; then
|
||||||
|
play_found=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$play_found" != true ]]; then
|
||||||
|
all_play_fingerprints_published=false
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [[ "$all_play_fingerprints_published" == true ]]; then
|
||||||
|
ready "Android App Links" "published identities include the Play App Signing certificate"
|
||||||
|
else
|
||||||
|
invalid "Android App Links" "Play App Signing fingerprint is absent from the published identities"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
partial "Android App Links" "package and signing fingerprints must be configured together"
|
partial "Android App Links" "package and signing fingerprints are incomplete"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
android_signing_alias=
|
android_signing_alias=
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ if ! printf '%s\n' "$domain" |
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for command in awk docker git grep mktemp openssl stat; do
|
for command in awk docker git grep mktemp openssl stat tr; do
|
||||||
if ! command -v "$command" >/dev/null 2>&1; then
|
if ! command -v "$command" >/dev/null 2>&1; then
|
||||||
echo "Required command is unavailable: $command" >&2
|
echo "Required command is unavailable: $command" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
|
@ -89,6 +89,7 @@ fcm_project_id=${PRODUCTION_FCM_PROJECT_ID:-}
|
||||||
fcm_service_account_json_base64=${PRODUCTION_FCM_SERVICE_ACCOUNT_JSON_BASE64:-}
|
fcm_service_account_json_base64=${PRODUCTION_FCM_SERVICE_ACCOUNT_JSON_BASE64:-}
|
||||||
android_app_links_package_name=${PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME:-}
|
android_app_links_package_name=${PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME:-}
|
||||||
android_app_links_fingerprints=${PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS:-}
|
android_app_links_fingerprints=${PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS:-}
|
||||||
|
android_play_app_signing_fingerprints=${PRODUCTION_ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS:-}
|
||||||
test_domain=${PRODUCTION_TEST_DOMAIN:-"test.$domain"}
|
test_domain=${PRODUCTION_TEST_DOMAIN:-"test.$domain"}
|
||||||
test_upstream=${PRODUCTION_TEST_UPSTREAM:-who-need-help-test:4000}
|
test_upstream=${PRODUCTION_TEST_UPSTREAM:-who-need-help-test:4000}
|
||||||
edge_compose_project_name=${PRODUCTION_EDGE_COMPOSE_PROJECT_NAME:-who_need_help_edge}
|
edge_compose_project_name=${PRODUCTION_EDGE_COMPOSE_PROJECT_NAME:-who_need_help_edge}
|
||||||
|
|
@ -114,6 +115,7 @@ require_single_line_env_value PRODUCTION_FCM_PROJECT_ID "$fcm_project_id"
|
||||||
require_single_line_env_value PRODUCTION_FCM_SERVICE_ACCOUNT_JSON_BASE64 "$fcm_service_account_json_base64"
|
require_single_line_env_value PRODUCTION_FCM_SERVICE_ACCOUNT_JSON_BASE64 "$fcm_service_account_json_base64"
|
||||||
require_single_line_env_value PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME "$android_app_links_package_name"
|
require_single_line_env_value PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME "$android_app_links_package_name"
|
||||||
require_single_line_env_value PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS "$android_app_links_fingerprints"
|
require_single_line_env_value PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS "$android_app_links_fingerprints"
|
||||||
|
require_single_line_env_value PRODUCTION_ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS "$android_play_app_signing_fingerprints"
|
||||||
require_single_line_env_value PRODUCTION_TEST_DOMAIN "$test_domain"
|
require_single_line_env_value PRODUCTION_TEST_DOMAIN "$test_domain"
|
||||||
require_single_line_env_value PRODUCTION_TEST_UPSTREAM "$test_upstream"
|
require_single_line_env_value PRODUCTION_TEST_UPSTREAM "$test_upstream"
|
||||||
require_single_line_env_value PRODUCTION_EDGE_COMPOSE_PROJECT_NAME "$edge_compose_project_name"
|
require_single_line_env_value PRODUCTION_EDGE_COMPOSE_PROJECT_NAME "$edge_compose_project_name"
|
||||||
|
|
@ -129,9 +131,17 @@ if { [ -n "$google_oauth_client_id" ] || [ -n "$google_oauth_client_secret" ]; }
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if { [ -n "$android_app_links_package_name" ] || [ -n "$android_app_links_fingerprints" ]; } &&
|
if {
|
||||||
{ [ -z "$android_app_links_package_name" ] || [ -z "$android_app_links_fingerprints" ]; }; then
|
[ -n "$android_app_links_package_name" ] ||
|
||||||
echo "Production Android App Links package and fingerprints must either both be set or both be empty." >&2
|
[ -n "$android_app_links_fingerprints" ] ||
|
||||||
|
[ -n "$android_play_app_signing_fingerprints" ]
|
||||||
|
} &&
|
||||||
|
{
|
||||||
|
[ -z "$android_app_links_package_name" ] ||
|
||||||
|
[ -z "$android_app_links_fingerprints" ] ||
|
||||||
|
[ -z "$android_play_app_signing_fingerprints" ]
|
||||||
|
}; then
|
||||||
|
echo "Production Android App Links require the package, published fingerprints, and Play App Signing fingerprints together." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -223,6 +233,42 @@ if [ -n "$android_app_links_package_name" ] &&
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ -n "$android_app_links_fingerprints" ]; then
|
||||||
|
if ! printf '%s\n' "$android_app_links_fingerprints" |
|
||||||
|
tr ',' '\n' |
|
||||||
|
while IFS= read -r fingerprint; do
|
||||||
|
compact_fingerprint=$(printf '%s' "$fingerprint" | tr -d ':[:space:]')
|
||||||
|
printf '%s\n' "$compact_fingerprint" |
|
||||||
|
grep -Eq '^[0-9A-Fa-f]{64}$' || exit 1
|
||||||
|
done; then
|
||||||
|
echo "PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS contains an invalid SHA-256 fingerprint." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
published_compact_fingerprints=$(
|
||||||
|
printf '%s' "$android_app_links_fingerprints" |
|
||||||
|
tr ',' '\n' |
|
||||||
|
tr -d ':[:space:]' |
|
||||||
|
tr '[:lower:]' '[:upper:]'
|
||||||
|
)
|
||||||
|
if ! printf '%s\n' "$android_play_app_signing_fingerprints" |
|
||||||
|
tr ',' '\n' |
|
||||||
|
while IFS= read -r play_fingerprint; do
|
||||||
|
compact_play_fingerprint=$(
|
||||||
|
printf '%s' "$play_fingerprint" |
|
||||||
|
tr -d ':[:space:]' |
|
||||||
|
tr '[:lower:]' '[:upper:]'
|
||||||
|
)
|
||||||
|
printf '%s\n' "$compact_play_fingerprint" |
|
||||||
|
grep -Eq '^[0-9A-F]{64}$' || exit 1
|
||||||
|
printf '%s\n' "$published_compact_fingerprints" |
|
||||||
|
grep -Fqx "$compact_play_fingerprint" || exit 1
|
||||||
|
done; then
|
||||||
|
echo "Every production Play App Signing fingerprint must also be published in the Android App Links fingerprint list." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
case "$compose_project_name" in
|
case "$compose_project_name" in
|
||||||
*[!a-zA-Z0-9_-]* | '')
|
*[!a-zA-Z0-9_-]* | '')
|
||||||
echo "PRODUCTION_COMPOSE_PROJECT_NAME must contain only letters, numbers, underscores, or hyphens." >&2
|
echo "PRODUCTION_COMPOSE_PROJECT_NAME must contain only letters, numbers, underscores, or hyphens." >&2
|
||||||
|
|
@ -360,6 +406,7 @@ FCM_PROJECT_ID_VALUE=$fcm_project_id \
|
||||||
FCM_SERVICE_ACCOUNT_JSON_BASE64_VALUE=$fcm_service_account_json_base64 \
|
FCM_SERVICE_ACCOUNT_JSON_BASE64_VALUE=$fcm_service_account_json_base64 \
|
||||||
ANDROID_APP_LINKS_PACKAGE_NAME_VALUE=$android_app_links_package_name \
|
ANDROID_APP_LINKS_PACKAGE_NAME_VALUE=$android_app_links_package_name \
|
||||||
ANDROID_APP_LINKS_FINGERPRINTS_VALUE=$android_app_links_fingerprints \
|
ANDROID_APP_LINKS_FINGERPRINTS_VALUE=$android_app_links_fingerprints \
|
||||||
|
ANDROID_PLAY_APP_SIGNING_FINGERPRINTS_VALUE=$android_play_app_signing_fingerprints \
|
||||||
EDGE_COMPOSE_PROJECT_NAME_VALUE=$edge_compose_project_name \
|
EDGE_COMPOSE_PROJECT_NAME_VALUE=$edge_compose_project_name \
|
||||||
PRIMARY_UPSTREAM_VALUE="$public_upstream_name:4000" \
|
PRIMARY_UPSTREAM_VALUE="$public_upstream_name:4000" \
|
||||||
TEST_DOMAIN_VALUE=$test_domain \
|
TEST_DOMAIN_VALUE=$test_domain \
|
||||||
|
|
@ -427,6 +474,7 @@ TEST_UPSTREAM_VALUE=$test_upstream \
|
||||||
replacement["FCM_SERVICE_ACCOUNT_JSON_BASE64"] = ENVIRON["FCM_SERVICE_ACCOUNT_JSON_BASE64_VALUE"]
|
replacement["FCM_SERVICE_ACCOUNT_JSON_BASE64"] = ENVIRON["FCM_SERVICE_ACCOUNT_JSON_BASE64_VALUE"]
|
||||||
replacement["ANDROID_APP_LINKS_PACKAGE_NAME"] = ENVIRON["ANDROID_APP_LINKS_PACKAGE_NAME_VALUE"]
|
replacement["ANDROID_APP_LINKS_PACKAGE_NAME"] = ENVIRON["ANDROID_APP_LINKS_PACKAGE_NAME_VALUE"]
|
||||||
replacement["ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS"] = ENVIRON["ANDROID_APP_LINKS_FINGERPRINTS_VALUE"]
|
replacement["ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS"] = ENVIRON["ANDROID_APP_LINKS_FINGERPRINTS_VALUE"]
|
||||||
|
replacement["ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS"] = ENVIRON["ANDROID_PLAY_APP_SIGNING_FINGERPRINTS_VALUE"]
|
||||||
replacement["WNH_ANDROID_SIGNING_KEY_ALIAS"] = "who-need-help-upload"
|
replacement["WNH_ANDROID_SIGNING_KEY_ALIAS"] = "who-need-help-upload"
|
||||||
replacement["WNH_ANDROID_DEVELOPMENT_SIGNING_KEY_ALIAS"] = ""
|
replacement["WNH_ANDROID_DEVELOPMENT_SIGNING_KEY_ALIAS"] = ""
|
||||||
replacement["WNH_ANDROID_STAGING_SIGNING_KEY_ALIAS"] = ""
|
replacement["WNH_ANDROID_STAGING_SIGNING_KEY_ALIAS"] = ""
|
||||||
|
|
|
||||||
|
|
@ -334,6 +334,7 @@ printf '%s\n' \
|
||||||
'WNH_BASE_URL=https://dev.help.test' \
|
'WNH_BASE_URL=https://dev.help.test' \
|
||||||
'ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.development' \
|
'ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.development' \
|
||||||
"ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=$android_fingerprint" \
|
"ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=$android_fingerprint" \
|
||||||
|
'ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=' \
|
||||||
>"$android_env"
|
>"$android_env"
|
||||||
chmod 600 "$android_env"
|
chmod 600 "$android_env"
|
||||||
./scripts/validate-android-environment.sh \
|
./scripts/validate-android-environment.sh \
|
||||||
|
|
@ -354,7 +355,7 @@ sed -i \
|
||||||
./scripts/validate-android-environment.sh "$android_env" test >/dev/null
|
./scripts/validate-android-environment.sh "$android_env" test >/dev/null
|
||||||
|
|
||||||
sed -i \
|
sed -i \
|
||||||
's|^DEPLOYMENT_ENV=.*|DEPLOYMENT_ENV=production|; s|^ANDROID_APP_LINKS_PACKAGE_NAME=.*|ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile|' \
|
"s|^DEPLOYMENT_ENV=.*|DEPLOYMENT_ENV=production|; s|^ANDROID_APP_LINKS_PACKAGE_NAME=.*|ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile|; s|^ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=.*|ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=$android_fingerprint|" \
|
||||||
"$android_env"
|
"$android_env"
|
||||||
./scripts/validate-android-environment.sh \
|
./scripts/validate-android-environment.sh \
|
||||||
"$android_env" production >/dev/null
|
"$android_env" production >/dev/null
|
||||||
|
|
@ -602,6 +603,7 @@ PRODUCTION_FCM_PROJECT_ID=quality-production \
|
||||||
PRODUCTION_FCM_SERVICE_ACCOUNT_JSON_BASE64="$quality_fcm_base64" \
|
PRODUCTION_FCM_SERVICE_ACCOUNT_JSON_BASE64="$quality_fcm_base64" \
|
||||||
PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile \
|
PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile \
|
||||||
PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \
|
PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \
|
||||||
|
PRODUCTION_ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \
|
||||||
PRODUCTION_SUPPORT_INBOX_ADDRESS=support@help.test \
|
PRODUCTION_SUPPORT_INBOX_ADDRESS=support@help.test \
|
||||||
./scripts/init-production-env.sh help.test "$production_env" >/dev/null
|
./scripts/init-production-env.sh help.test "$production_env" >/dev/null
|
||||||
test "$(stat -c '%a' "$production_env")" = 600
|
test "$(stat -c '%a' "$production_env")" = 600
|
||||||
|
|
@ -641,11 +643,21 @@ fi
|
||||||
if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \
|
if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \
|
||||||
PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.staging \
|
PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile.staging \
|
||||||
PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \
|
PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \
|
||||||
|
PRODUCTION_ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \
|
||||||
./scripts/init-production-env.sh help.test \
|
./scripts/init-production-env.sh help.test \
|
||||||
"$scan_dir/production.staging-package.env" >/dev/null 2>&1; then
|
"$scan_dir/production.staging-package.env" >/dev/null 2>&1; then
|
||||||
echo "Production environment initializer accepted the staging Android package." >&2
|
echo "Production environment initializer accepted the staging Android package." >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \
|
||||||
|
PRODUCTION_ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile \
|
||||||
|
PRODUCTION_ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB \
|
||||||
|
PRODUCTION_ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=D7:C4:F1:12:4D:F4:68:E5:B3:54:DE:D8:96:E8:01:51:29:41:F1:8A:71:0C:18:B0:E7:98:AA:2B:81:DA:11:DF \
|
||||||
|
./scripts/init-production-env.sh help.test \
|
||||||
|
"$scan_dir/production.unpublished-play-signing.env" >/dev/null 2>&1; then
|
||||||
|
echo "Production environment initializer accepted a Play fingerprint absent from assetlinks." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \
|
if PRODUCTION_CODEX_SESSION_ID=00000000-0000-0000-0000-000000000001 \
|
||||||
PRODUCTION_WEB_PUSH_VAPID_PUBLIC_KEY=quality-vapid-public \
|
PRODUCTION_WEB_PUSH_VAPID_PUBLIC_KEY=quality-vapid-public \
|
||||||
PRODUCTION_WEB_PUSH_VAPID_PRIVATE_KEY=quality-vapid-private \
|
PRODUCTION_WEB_PUSH_VAPID_PRIVATE_KEY=quality-vapid-private \
|
||||||
|
|
@ -739,6 +751,38 @@ if ./scripts/check-environment-readiness.sh \
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
upload_only_app_links_env="$scan_dir/production.upload-only-app-links.env"
|
||||||
|
cp "$production_env" "$upload_only_app_links_env"
|
||||||
|
sed -i \
|
||||||
|
's|^ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=.*|ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=|' \
|
||||||
|
"$upload_only_app_links_env"
|
||||||
|
if ./scripts/validate-production-env.sh \
|
||||||
|
"$upload_only_app_links_env" help.test >/dev/null 2>&1; then
|
||||||
|
echo "Production validation accepted upload-only Android App Links." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ./scripts/check-environment-readiness.sh \
|
||||||
|
"$upload_only_app_links_env" --require-release >/dev/null 2>&1; then
|
||||||
|
echo "Environment readiness accepted upload-only Android App Links." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
unpublished_play_app_links_env="$scan_dir/production.unpublished-play-app-links.env"
|
||||||
|
cp "$production_env" "$unpublished_play_app_links_env"
|
||||||
|
sed -i \
|
||||||
|
's|^ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=.*|ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=D7:C4:F1:12:4D:F4:68:E5:B3:54:DE:D8:96:E8:01:51:29:41:F1:8A:71:0C:18:B0:E7:98:AA:2B:81:DA:11:DF|' \
|
||||||
|
"$unpublished_play_app_links_env"
|
||||||
|
if ./scripts/validate-production-env.sh \
|
||||||
|
"$unpublished_play_app_links_env" help.test >/dev/null 2>&1; then
|
||||||
|
echo "Production validation accepted an unpublished Play App Signing fingerprint." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ./scripts/check-environment-readiness.sh \
|
||||||
|
"$unpublished_play_app_links_env" --require-release >/dev/null 2>&1; then
|
||||||
|
echo "Environment readiness accepted an unpublished Play App Signing fingerprint." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
grep -Fx 'COMPOSE_PROJECT_NAME=who_need_help_production' "$production_env" >/dev/null
|
grep -Fx 'COMPOSE_PROJECT_NAME=who_need_help_production' "$production_env" >/dev/null
|
||||||
grep -E '^APP_IMAGE=who-need-help:production-[0-9a-f]{12}$' "$production_env" >/dev/null
|
grep -E '^APP_IMAGE=who-need-help:production-[0-9a-f]{12}$' "$production_env" >/dev/null
|
||||||
grep -Fx 'EDGE_COMPOSE_PROJECT_NAME=who_need_help_edge' "$production_env" >/dev/null
|
grep -Fx 'EDGE_COMPOSE_PROJECT_NAME=who_need_help_edge' "$production_env" >/dev/null
|
||||||
|
|
@ -746,6 +790,7 @@ grep -Fx 'PRIMARY_DOMAIN=help.test' "$production_env" >/dev/null
|
||||||
grep -Fx 'TEST_DOMAIN=test.help.test' "$production_env" >/dev/null
|
grep -Fx 'TEST_DOMAIN=test.help.test' "$production_env" >/dev/null
|
||||||
grep -Fx 'ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile' "$production_env" >/dev/null
|
grep -Fx 'ANDROID_APP_LINKS_PACKAGE_NAME=org.whoneedhelp.mobile' "$production_env" >/dev/null
|
||||||
grep -Fx 'ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB' "$production_env" >/dev/null
|
grep -Fx 'ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB' "$production_env" >/dev/null
|
||||||
|
grep -Fx 'ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS=A5:74:2B:AE:70:C6:D0:34:E3:75:44:B6:2E:37:A3:75:C0:E0:05:64:74:50:F4:0F:29:B2:A9:84:F9:FD:B8:FB' "$production_env" >/dev/null
|
||||||
./scripts/validate-edge-env.sh "$production_env" >/dev/null
|
./scripts/validate-edge-env.sh "$production_env" >/dev/null
|
||||||
|
|
||||||
test_checkout="$scan_dir/test-checkout"
|
test_checkout="$scan_dir/test-checkout"
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,12 @@ phx_port=$(read_unique PHX_URL_PORT)
|
||||||
base_url=$(read_unique WNH_BASE_URL)
|
base_url=$(read_unique WNH_BASE_URL)
|
||||||
app_links_package=$(read_unique ANDROID_APP_LINKS_PACKAGE_NAME)
|
app_links_package=$(read_unique ANDROID_APP_LINKS_PACKAGE_NAME)
|
||||||
app_links_fingerprints=$(read_unique ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS)
|
app_links_fingerprints=$(read_unique ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS)
|
||||||
|
play_app_signing_fingerprints=
|
||||||
|
if [[ "$expected_environment" == production ]]; then
|
||||||
|
play_app_signing_fingerprints=$(
|
||||||
|
read_unique ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
[[ "$deployment_environment" == "$expected_environment" ]] || {
|
[[ "$deployment_environment" == "$expected_environment" ]] || {
|
||||||
echo "Android build requires DEPLOYMENT_ENV=$expected_environment." >&2
|
echo "Android build requires DEPLOYMENT_ENV=$expected_environment." >&2
|
||||||
|
|
@ -107,4 +113,30 @@ for fingerprint in "${fingerprints[@]}"; do
|
||||||
}
|
}
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if [[ "$expected_environment" == production ]]; then
|
||||||
|
IFS=',' read -r -a play_fingerprints <<<"$play_app_signing_fingerprints"
|
||||||
|
for play_fingerprint in "${play_fingerprints[@]}"; do
|
||||||
|
compact_play=${play_fingerprint//:/}
|
||||||
|
compact_play=${compact_play//[[:space:]]/}
|
||||||
|
[[ "$compact_play" =~ ^[0-9A-Fa-f]{64}$ ]] || {
|
||||||
|
echo "ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS contains an invalid value." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
play_found=false
|
||||||
|
for fingerprint in "${fingerprints[@]}"; do
|
||||||
|
compact=${fingerprint//:/}
|
||||||
|
compact=${compact//[[:space:]]/}
|
||||||
|
if [[ "${compact^^}" == "${compact_play^^}" ]]; then
|
||||||
|
play_found=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[[ "$play_found" == true ]] || {
|
||||||
|
echo "The Play App Signing fingerprint is absent from the published App Links identities." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Verified $expected_environment Android origin, application ID, and App Links identity."
|
echo "Verified $expected_environment Android origin, application ID, and App Links identity."
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ fcm_service_account_file=$(optional_value FCM_SERVICE_ACCOUNT_FILE)
|
||||||
fcm_service_account_json_base64=$(optional_value FCM_SERVICE_ACCOUNT_JSON_BASE64)
|
fcm_service_account_json_base64=$(optional_value FCM_SERVICE_ACCOUNT_JSON_BASE64)
|
||||||
android_app_links_package_name=$(optional_value ANDROID_APP_LINKS_PACKAGE_NAME)
|
android_app_links_package_name=$(optional_value ANDROID_APP_LINKS_PACKAGE_NAME)
|
||||||
android_app_links_fingerprints=$(optional_value ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS)
|
android_app_links_fingerprints=$(optional_value ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS)
|
||||||
|
android_play_app_signing_fingerprints=$(optional_value ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS)
|
||||||
codex_session_id=$(require_value CODEX_SESSION_ID)
|
codex_session_id=$(require_value CODEX_SESSION_ID)
|
||||||
edge_compose_project_name=$(require_value EDGE_COMPOSE_PROJECT_NAME)
|
edge_compose_project_name=$(require_value EDGE_COMPOSE_PROJECT_NAME)
|
||||||
caddy_image=$(require_value CADDY_IMAGE)
|
caddy_image=$(require_value CADDY_IMAGE)
|
||||||
|
|
@ -444,9 +445,13 @@ if [[ -n "$fcm_project_id" || -n "$fcm_service_account_file" ||
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "$android_app_links_package_name" || -n "$android_app_links_fingerprints" ]]; then
|
if [[ -n "$android_app_links_package_name" ||
|
||||||
[[ -n "$android_app_links_package_name" && -n "$android_app_links_fingerprints" ]] || {
|
-n "$android_app_links_fingerprints" ||
|
||||||
echo "Android App Links package and fingerprints must either both be set or both be empty." >&2
|
-n "$android_play_app_signing_fingerprints" ]]; then
|
||||||
|
[[ -n "$android_app_links_package_name" &&
|
||||||
|
-n "$android_app_links_fingerprints" &&
|
||||||
|
-n "$android_play_app_signing_fingerprints" ]] || {
|
||||||
|
echo "Production Android App Links require the package, published fingerprints, and Play App Signing fingerprints together." >&2
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
[[ "$android_app_links_package_name" =~ ^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$ ]] || {
|
[[ "$android_app_links_package_name" =~ ^[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)+$ ]] || {
|
||||||
|
|
@ -471,6 +476,34 @@ if [[ -n "$android_app_links_package_name" || -n "$android_app_links_fingerprint
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
done
|
done
|
||||||
|
|
||||||
|
IFS=',' read -r -a play_fingerprints <<<"$android_play_app_signing_fingerprints"
|
||||||
|
[[ ${#play_fingerprints[@]} -gt 0 ]] || {
|
||||||
|
echo "ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS is empty." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
for play_fingerprint in "${play_fingerprints[@]}"; do
|
||||||
|
compact_play_fingerprint=${play_fingerprint//:/}
|
||||||
|
compact_play_fingerprint=${compact_play_fingerprint//[[:space:]]/}
|
||||||
|
[[ "$compact_play_fingerprint" =~ ^[0-9A-Fa-f]{64}$ ]] || {
|
||||||
|
echo "ANDROID_PLAY_APP_SIGNING_SHA256_CERT_FINGERPRINTS contains an invalid SHA-256 fingerprint." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
play_fingerprint_found=false
|
||||||
|
for fingerprint in "${android_fingerprints[@]}"; do
|
||||||
|
compact_fingerprint=${fingerprint//:/}
|
||||||
|
compact_fingerprint=${compact_fingerprint//[[:space:]]/}
|
||||||
|
if [[ "${compact_fingerprint^^}" == "${compact_play_fingerprint^^}" ]]; then
|
||||||
|
play_fingerprint_found=true
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
[[ "$play_fingerprint_found" == true ]] || {
|
||||||
|
echo "Every Play App Signing fingerprint must also be published in ANDROID_APP_LINKS_SHA256_CERT_FINGERPRINTS." >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
case "$codex_session_id" in
|
case "$codex_session_id" in
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user