mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-05 10:29:58 +02:00
Merge pull request #39 from sudosylabs/feat/release-pipeline-fixes
fix(release): repair Apple and Android builds
This commit is contained in:
2
.github/workflows/apple-release.yml
vendored
2
.github/workflows/apple-release.yml
vendored
@@ -132,7 +132,7 @@ jobs:
|
|||||||
echo "SPARKLE_ED_KEY_FILE=$RUNNER_TEMP/sparkle_ed_private_key" >> "$GITHUB_ENV"
|
echo "SPARKLE_ED_KEY_FILE=$RUNNER_TEMP/sparkle_ed_private_key" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
- name: Build, sign & notarize DMG
|
- name: Build, sign & notarize DMG
|
||||||
run: apple/scripts/build-dmg.sh
|
run: make build-apple-dmg
|
||||||
|
|
||||||
- name: Generate appcast
|
- name: Generate appcast
|
||||||
env:
|
env:
|
||||||
|
|||||||
31
.github/workflows/release.yml
vendored
31
.github/workflows/release.yml
vendored
@@ -185,37 +185,19 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
apk="build/release/play/VniDrop-${VERSION}-${VERSION_CODE}-play-universal.apk"
|
apk="build/release/play/VniDrop-${VERSION}-${VERSION_CODE}-play-universal.apk"
|
||||||
apksigner_path="$(
|
|
||||||
find "$ANDROID_SDK_ROOT/build-tools" -type f -name apksigner -perm -111 |
|
|
||||||
sort -r |
|
|
||||||
head -1
|
|
||||||
)"
|
|
||||||
apkanalyzer_path="$(
|
apkanalyzer_path="$(
|
||||||
find "$ANDROID_SDK_ROOT/cmdline-tools" -type f -name apkanalyzer -perm -111 |
|
find "$ANDROID_SDK_ROOT/cmdline-tools" -type f -name apkanalyzer -perm -111 |
|
||||||
sort -r |
|
sort -r |
|
||||||
head -1
|
head -1
|
||||||
)"
|
)"
|
||||||
if [ -z "$apksigner_path" ] || [ -z "$apkanalyzer_path" ]; then
|
if [ -z "$apkanalyzer_path" ]; then
|
||||||
echo "Android SDK verification tools were not found" >&2
|
echo "apkanalyzer was not found" >&2
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
"$apksigner_path" verify --verbose --print-certs "$apk" \
|
|
||||||
> build/release/play/apksigner-report.txt
|
|
||||||
actual="$(
|
|
||||||
awk -F': ' '/Signer #1 certificate SHA-256 digest:/ {print $2; exit}' \
|
|
||||||
build/release/play/apksigner-report.txt |
|
|
||||||
tr -d '[:space:]:' |
|
|
||||||
tr '[:upper:]' '[:lower:]'
|
|
||||||
)"
|
|
||||||
expected="$(
|
|
||||||
printf '%s' "$EXPECTED_CERT_SHA256" |
|
|
||||||
tr -d '[:space:]:' |
|
|
||||||
tr '[:upper:]' '[:lower:]'
|
|
||||||
)"
|
|
||||||
if [ -z "$actual" ] || [ "$actual" != "$expected" ]; then
|
|
||||||
echo "Play APK signing certificate mismatch" >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
packaging/android/verify-apk-signature.sh \
|
||||||
|
"$apk" \
|
||||||
|
"$EXPECTED_CERT_SHA256" \
|
||||||
|
>/dev/null
|
||||||
if [ "$("$apkanalyzer_path" manifest application-id "$apk")" != "com.vnidrop.app" ]; then
|
if [ "$("$apkanalyzer_path" manifest application-id "$apk")" != "com.vnidrop.app" ]; then
|
||||||
echo "Play APK package name mismatch" >&2
|
echo "Play APK package name mismatch" >&2
|
||||||
exit 1
|
exit 1
|
||||||
@@ -228,7 +210,6 @@ jobs:
|
|||||||
echo "Play APK version code mismatch" >&2
|
echo "Play APK version code mismatch" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
rm build/release/play/apksigner-report.txt
|
|
||||||
(
|
(
|
||||||
cd build/release/play
|
cd build/release/play
|
||||||
sha256sum \
|
sha256sum \
|
||||||
|
|||||||
6
Makefile
6
Makefile
@@ -71,8 +71,10 @@ check-version: ## Validate the canonical version and its platform mappings.
|
|||||||
cd $(ROOT) && $(GRADLE) verifyVersion $(GRADLE_FLAGS)
|
cd $(ROOT) && $(GRADLE) verifyVersion $(GRADLE_FLAGS)
|
||||||
|
|
||||||
check-release: ## Validate coordinated release scripts and workflow YAML.
|
check-release: ## Validate coordinated release scripts and workflow YAML.
|
||||||
cd $(ROOT) && bash -n packaging/android/build-release.sh packaging/release/assemble-release.sh packaging/release/test-assemble-release.sh
|
cd $(ROOT) && bash -n packaging/android/build-release.sh packaging/android/verify-apk-signature.sh packaging/android/tests/test_verify_apk_signature.sh packaging/release/assemble-release.sh packaging/release/test-assemble-release.sh packaging/release/test-release-config.sh
|
||||||
|
cd $(ROOT) && packaging/android/tests/test_verify_apk_signature.sh
|
||||||
cd $(ROOT) && packaging/release/test-assemble-release.sh
|
cd $(ROOT) && packaging/release/test-assemble-release.sh
|
||||||
|
cd $(ROOT) && packaging/release/test-release-config.sh
|
||||||
cd $(ROOT) && python3 -m unittest discover -s packaging/android/tests -v
|
cd $(ROOT) && python3 -m unittest discover -s packaging/android/tests -v
|
||||||
cd $(ROOT) && ruby -e 'require "yaml"; ARGV.each { |file| YAML.load_file(file) }' .github/workflows/*.yml
|
cd $(ROOT) && ruby -e 'require "yaml"; ARGV.each { |file| YAML.load_file(file) }' .github/workflows/*.yml
|
||||||
|
|
||||||
@@ -143,7 +145,7 @@ build-apple-macos: apple-project ## Build the native macOS app (unsigned by defa
|
|||||||
build-apple-macos-direct: apple-project ## Build the direct-download macOS target (Sparkle, unsigned) — CI compile check.
|
build-apple-macos-direct: apple-project ## Build the direct-download macOS target (Sparkle, unsigned) — CI compile check.
|
||||||
cd $(ROOT)/apple && $(XCODEBUILD) -project VniDrop.xcodeproj -scheme VniDropDirect -configuration Release-Direct -derivedDataPath "$(APPLE_DERIVED_DATA)" -destination 'platform=macOS' CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO build
|
cd $(ROOT)/apple && $(XCODEBUILD) -project VniDrop.xcodeproj -scheme VniDropDirect -configuration Release-Direct -derivedDataPath "$(APPLE_DERIVED_DATA)" -destination 'platform=macOS' CODE_SIGNING_ALLOWED=NO CODE_SIGNING_REQUIRED=NO build
|
||||||
|
|
||||||
build-apple-dmg: ## Build the signed/notarized direct-download .dmg (see apple/RELEASE-MACOS.md for required env).
|
build-apple-dmg: localization ## Build the signed/notarized direct-download .dmg (see apple/RELEASE-MACOS.md for required env).
|
||||||
cd $(ROOT) && apple/scripts/build-dmg.sh
|
cd $(ROOT) && apple/scripts/build-dmg.sh
|
||||||
|
|
||||||
open-apple: build-apple-macos ## Build and launch the native macOS app.
|
open-apple: build-apple-macos ## Build and launch the native macOS app.
|
||||||
|
|||||||
@@ -53,18 +53,6 @@ verify_archive_entries() {
|
|||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
find_apksigner() {
|
|
||||||
if command -v apksigner >/dev/null 2>&1; then
|
|
||||||
command -v apksigner
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
local sdk_root=${ANDROID_SDK_ROOT:-${ANDROID_HOME:-}}
|
|
||||||
[[ -n $sdk_root ]] || return 1
|
|
||||||
find "$sdk_root/build-tools" -type f -name apksigner -perm -111 2>/dev/null |
|
|
||||||
sort -r |
|
|
||||||
head -1
|
|
||||||
}
|
|
||||||
|
|
||||||
for name in \
|
for name in \
|
||||||
VNIDROP_ANDROID_KEYSTORE_PATH \
|
VNIDROP_ANDROID_KEYSTORE_PATH \
|
||||||
VNIDROP_ANDROID_KEYSTORE_PASSWORD \
|
VNIDROP_ANDROID_KEYSTORE_PASSWORD \
|
||||||
@@ -119,26 +107,12 @@ grep -F 'jar verified.' <<< "$jarsigner_report" >/dev/null || {
|
|||||||
}
|
}
|
||||||
verify_archive_entries "$source_apk" "${required_apk_libraries[@]}"
|
verify_archive_entries "$source_apk" "${required_apk_libraries[@]}"
|
||||||
verify_archive_entries "$source_aab" "${required_aab_libraries[@]}"
|
verify_archive_entries "$source_aab" "${required_aab_libraries[@]}"
|
||||||
apksigner_path="$(find_apksigner)" || {
|
|
||||||
printf 'apksigner was not found in PATH or the Android SDK\n' >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
signature_report="$("$apksigner_path" verify --verbose --print-certs "$source_apk")"
|
|
||||||
actual_fingerprint="$(
|
actual_fingerprint="$(
|
||||||
printf '%s\n' "$signature_report" |
|
"$script_dir/verify-apk-signature.sh" \
|
||||||
awk -F': ' '/Signer #1 certificate SHA-256 digest:/ {print $2; exit}'
|
"$source_apk" \
|
||||||
|
"$VNIDROP_ANDROID_UPLOAD_CERT_SHA256"
|
||||||
)"
|
)"
|
||||||
[[ -n $actual_fingerprint ]] || {
|
|
||||||
printf 'Could not read the APK signing certificate fingerprint\n' >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
actual_fingerprint="$(normalize_fingerprint "$actual_fingerprint")"
|
|
||||||
expected_fingerprint="$(normalize_fingerprint "$VNIDROP_ANDROID_UPLOAD_CERT_SHA256")"
|
expected_fingerprint="$(normalize_fingerprint "$VNIDROP_ANDROID_UPLOAD_CERT_SHA256")"
|
||||||
[[ $actual_fingerprint == "$expected_fingerprint" ]] || {
|
|
||||||
printf 'APK signing certificate mismatch: expected %s, got %s\n' \
|
|
||||||
"$expected_fingerprint" "$actual_fingerprint" >&2
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
aab_fingerprint="$(
|
aab_fingerprint="$(
|
||||||
keytool -printcert -jarfile "$source_aab" |
|
keytool -printcert -jarfile "$source_aab" |
|
||||||
awk -F': ' '/SHA256:/ {print $2; exit}'
|
awk -F': ' '/SHA256:/ {print $2; exit}'
|
||||||
|
|||||||
57
packaging/android/tests/test_verify_apk_signature.sh
Executable file
57
packaging/android/tests/test_verify_apk_signature.sh
Executable file
@@ -0,0 +1,57 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
verifier="$script_dir/../verify-apk-signature.sh"
|
||||||
|
scratch="$(mktemp -d "${TMPDIR:-/tmp}/vnidrop-apksigner-test.XXXXXX")"
|
||||||
|
trap 'rm -rf "$scratch"' EXIT
|
||||||
|
|
||||||
|
apk="$scratch/app.apk"
|
||||||
|
fake_apksigner="$scratch/apksigner"
|
||||||
|
printf 'apk\n' > "$apk"
|
||||||
|
|
||||||
|
cat > "$fake_apksigner" <<'SCRIPT'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
case "${FAKE_APKSIGNER_MODE:-success}" in
|
||||||
|
success)
|
||||||
|
printf '%s\n' \
|
||||||
|
'Verifies' \
|
||||||
|
'Signer #1 certificate SHA-256 digest: AA:BB:CC:DD' >&2
|
||||||
|
;;
|
||||||
|
missing)
|
||||||
|
printf '%s\n' 'Verifies' >&2
|
||||||
|
;;
|
||||||
|
failure)
|
||||||
|
printf '%s\n' 'invalid APK signature' >&2
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
SCRIPT
|
||||||
|
chmod +x "$fake_apksigner"
|
||||||
|
|
||||||
|
actual="$(
|
||||||
|
APKSIGNER="$fake_apksigner" \
|
||||||
|
"$verifier" "$apk" "aa bb cc dd"
|
||||||
|
)"
|
||||||
|
[[ $actual == aabbccdd ]]
|
||||||
|
|
||||||
|
if APKSIGNER="$fake_apksigner" \
|
||||||
|
"$verifier" "$apk" deadbeef >/dev/null 2>&1; then
|
||||||
|
printf 'Expected a certificate mismatch to fail\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if FAKE_APKSIGNER_MODE=missing APKSIGNER="$fake_apksigner" \
|
||||||
|
"$verifier" "$apk" aabbccdd >/dev/null 2>&1; then
|
||||||
|
printf 'Expected missing certificate output to fail\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if FAKE_APKSIGNER_MODE=failure APKSIGNER="$fake_apksigner" \
|
||||||
|
"$verifier" "$apk" aabbccdd >/dev/null 2>&1; then
|
||||||
|
printf 'Expected signature verification failure to propagate\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'APK signature verifier tests passed.\n'
|
||||||
86
packaging/android/verify-apk-signature.sh
Executable file
86
packaging/android/verify-apk-signature.sh
Executable file
@@ -0,0 +1,86 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [[ $# -ne 2 ]]; then
|
||||||
|
printf 'Usage: %s <apk> <expected-certificate-sha256>\n' "$0" >&2
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
apk=$1
|
||||||
|
expected_fingerprint=$2
|
||||||
|
build_tools_version=${ANDROID_BUILD_TOOLS_VERSION:-36.0.0}
|
||||||
|
|
||||||
|
normalize_fingerprint() {
|
||||||
|
printf '%s' "$1" |
|
||||||
|
tr -d '[:space:]:' |
|
||||||
|
tr '[:upper:]' '[:lower:]'
|
||||||
|
}
|
||||||
|
|
||||||
|
find_apksigner() {
|
||||||
|
if [[ -n ${APKSIGNER:-} ]]; then
|
||||||
|
[[ -x $APKSIGNER ]] || {
|
||||||
|
printf 'Configured apksigner is not executable: %s\n' "$APKSIGNER" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
printf '%s\n' "$APKSIGNER"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
local sdk_root=${ANDROID_SDK_ROOT:-${ANDROID_HOME:-}}
|
||||||
|
if [[ -n $sdk_root ]]; then
|
||||||
|
local pinned="$sdk_root/build-tools/$build_tools_version/apksigner"
|
||||||
|
if [[ -x $pinned ]]; then
|
||||||
|
printf '%s\n' "$pinned"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v apksigner >/dev/null 2>&1; then
|
||||||
|
command -v apksigner
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'apksigner %s was not found in the Android SDK or PATH\n' \
|
||||||
|
"$build_tools_version" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ -s $apk ]] || {
|
||||||
|
printf 'APK is missing or empty: %s\n' "$apk" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
apksigner_path="$(find_apksigner)" || exit 1
|
||||||
|
if ! signature_report="$(
|
||||||
|
"$apksigner_path" verify --verbose --print-certs "$apk" 2>&1
|
||||||
|
)"; then
|
||||||
|
printf 'APK signature verification failed:\n%s\n' "$signature_report" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
actual_fingerprint="$(
|
||||||
|
printf '%s\n' "$signature_report" |
|
||||||
|
awk '
|
||||||
|
tolower($0) ~ /^signer #1 certificate sha-256 digest:[[:space:]]*/ {
|
||||||
|
line = $0
|
||||||
|
sub(/^[^:]*:[[:space:]]*/, "", line)
|
||||||
|
print line
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
'
|
||||||
|
)"
|
||||||
|
[[ -n $actual_fingerprint ]] || {
|
||||||
|
printf 'Could not read the APK signing certificate fingerprint\n' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_fingerprint="$(normalize_fingerprint "$actual_fingerprint")"
|
||||||
|
expected_fingerprint="$(normalize_fingerprint "$expected_fingerprint")"
|
||||||
|
[[ $actual_fingerprint == "$expected_fingerprint" ]] || {
|
||||||
|
printf 'APK signing certificate mismatch: expected %s, got %s\n' \
|
||||||
|
"$expected_fingerprint" "$actual_fingerprint" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
printf '%s\n' "$actual_fingerprint"
|
||||||
28
packaging/release/test-release-config.sh
Executable file
28
packaging/release/test-release-config.sh
Executable file
@@ -0,0 +1,28 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
repo_root="$(cd "$script_dir/../.." && pwd)"
|
||||||
|
|
||||||
|
dry_run="$(make -n -C "$repo_root" build-apple-dmg)"
|
||||||
|
localization_line="$(
|
||||||
|
printf '%s\n' "$dry_run" |
|
||||||
|
awk '/bun run generate/ {print NR; exit}'
|
||||||
|
)"
|
||||||
|
build_line="$(
|
||||||
|
printf '%s\n' "$dry_run" |
|
||||||
|
awk '/apple\/scripts\/build-dmg\.sh/ {print NR; exit}'
|
||||||
|
)"
|
||||||
|
[[ -n $localization_line && -n $build_line && $localization_line -lt $build_line ]] || {
|
||||||
|
printf 'build-apple-dmg must generate localization before building the DMG\n' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
grep -F 'run: make build-apple-dmg' \
|
||||||
|
"$repo_root/.github/workflows/apple-release.yml" >/dev/null || {
|
||||||
|
printf 'Apple release workflow must use the generated-input-aware Make target\n' >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
printf 'Release configuration tests passed.\n'
|
||||||
Reference in New Issue
Block a user