mirror of
https://git.quad4.io/RNS-Things/MeshChatX.git
synced 2026-04-26 23:55:46 +00:00
feat(scripts): add Argos Translate script for JSON localization and new APK signing script
This commit is contained in:
Executable
+266
@@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Argos Translate JSON localization script.
|
||||
|
||||
This script provides an automated workflow to translate JSON localization files
|
||||
(such as `en.json`) to target languages using Argos Translate. It ensures that
|
||||
interpolated variables (e.g., `{count}`, `{status}`) are preserved and not
|
||||
altered during the translation process.
|
||||
|
||||
Requirements:
|
||||
- argostranslate (pip install argostranslate)
|
||||
|
||||
Usage:
|
||||
python scripts/argos_translate.py --from en --to zh --input locales/en.json --output locales/zh.json
|
||||
|
||||
You can also use environment variables instead of CLI flags:
|
||||
ARGOS_FROM_LANG="en"
|
||||
ARGOS_TO_LANG="zh"
|
||||
ARGOS_INPUT_FILE="locales/en.json"
|
||||
ARGOS_OUTPUT_FILE="locales/zh.json"
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
|
||||
|
||||
# ANSI color codes for terminal output
|
||||
class Colors:
|
||||
HEADER = "\033[95m"
|
||||
OKBLUE = "\033[94m"
|
||||
OKCYAN = "\033[96m"
|
||||
OKGREEN = "\033[92m"
|
||||
WARNING = "\033[93m"
|
||||
FAIL = "\033[91m"
|
||||
ENDC = "\033[0m"
|
||||
BOLD = "\033[1m"
|
||||
UNDERLINE = "\033[4m"
|
||||
|
||||
|
||||
def print_info(msg):
|
||||
print(f"{Colors.OKCYAN}[INFO]{Colors.ENDC} {msg}")
|
||||
|
||||
|
||||
def print_success(msg):
|
||||
print(f"{Colors.OKGREEN}[SUCCESS]{Colors.ENDC} {msg}")
|
||||
|
||||
|
||||
def print_warning(msg):
|
||||
print(f"{Colors.WARNING}[WARNING]{Colors.ENDC} {msg}")
|
||||
|
||||
|
||||
def print_error(msg):
|
||||
print(f"{Colors.FAIL}[ERROR]{Colors.ENDC} {msg}")
|
||||
|
||||
|
||||
try:
|
||||
import argostranslate.package
|
||||
import argostranslate.translate
|
||||
except ImportError:
|
||||
print_error("The 'argostranslate' module is not installed.")
|
||||
print_info("Please install it using: pip install argostranslate")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def ensure_package_installed(from_code, to_code):
|
||||
"""Ensure the translation package from `from_code` to `to_code` is installed.
|
||||
|
||||
If not installed, attempts to download and install it automatically.
|
||||
"""
|
||||
installed = argostranslate.translate.get_installed_languages()
|
||||
installed_dict = {lang.code: lang for lang in installed}
|
||||
|
||||
from_lang = installed_dict.get(from_code)
|
||||
to_lang = installed_dict.get(to_code)
|
||||
|
||||
if not from_lang or not to_lang or to_lang not in from_lang.translations_from:
|
||||
print_warning(
|
||||
f"Translation package {from_code} -> {to_code} not found. Attempting to install..."
|
||||
)
|
||||
try:
|
||||
argostranslate.package.update_package_index()
|
||||
available_packages = argostranslate.package.get_available_packages()
|
||||
|
||||
pkg_to_install = None
|
||||
for pkg in available_packages:
|
||||
if pkg.from_code == from_code and pkg.to_code == to_code:
|
||||
pkg_to_install = pkg
|
||||
break
|
||||
|
||||
if pkg_to_install:
|
||||
print_info(f"Downloading package: {pkg_to_install}")
|
||||
argostranslate.package.install_from_path(pkg_to_install.download())
|
||||
print_success(
|
||||
f"Successfully installed package: {from_code} -> {to_code}"
|
||||
)
|
||||
|
||||
# Refresh installed languages
|
||||
installed = argostranslate.translate.get_installed_languages()
|
||||
installed_dict = {lang.code: lang for lang in installed}
|
||||
else:
|
||||
print_error(
|
||||
f"Could not find a translation package for {from_code} -> {to_code}"
|
||||
)
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print_error(f"Failed to install language package: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
return installed_dict.get(from_code), installed_dict.get(to_code)
|
||||
|
||||
|
||||
def get_translation_func(from_code, to_code):
|
||||
"""Returns a translation function for the specified language pair."""
|
||||
from_lang, to_lang = ensure_package_installed(from_code, to_code)
|
||||
translator = from_lang.get_translation(to_lang)
|
||||
if not translator:
|
||||
print_error(f"No translation available from {from_code} to {to_code}")
|
||||
sys.exit(1)
|
||||
return translator.translate
|
||||
|
||||
|
||||
def replace_vars_with_tokens(text):
|
||||
"""Replaces `{variable}` patterns with a standard token like `XVAR0X`.
|
||||
|
||||
So the translation engine doesn't attempt to translate variable names.
|
||||
Returns the modified text and the list of found variables.
|
||||
"""
|
||||
vars_found = []
|
||||
|
||||
def replacer(match):
|
||||
vars_found.append(match.group(0))
|
||||
return f" XVAR{len(vars_found) - 1}X "
|
||||
|
||||
replaced_text = re.sub(r"\{+.*?\}+", replacer, text)
|
||||
return replaced_text, vars_found
|
||||
|
||||
|
||||
def restore_vars_from_tokens(text, vars_found):
|
||||
"""Restores the original `{variable}` patterns back into the translated text.
|
||||
|
||||
Looks for the `XVAR0X` tokens.
|
||||
"""
|
||||
for i, var in enumerate(vars_found):
|
||||
# The translation engine might change case or spacing around the token
|
||||
text = re.sub(rf"\s*[xX][vV][aA][rR]{i}[xX]\s*", var, text)
|
||||
return text.strip()
|
||||
|
||||
|
||||
def translate_dict(data, translate_func, target_name=None):
|
||||
"""Recursively iterates over a dictionary and translates all string values.
|
||||
|
||||
Skips the `_languageName` key, which can be explicitly set.
|
||||
"""
|
||||
if isinstance(data, dict):
|
||||
new_dict = {}
|
||||
for k, v in data.items():
|
||||
if k == "_languageName" and target_name:
|
||||
new_dict[k] = target_name
|
||||
continue
|
||||
elif k == "_languageName":
|
||||
# Keep original if no target name provided
|
||||
new_dict[k] = v
|
||||
continue
|
||||
|
||||
new_dict[k] = translate_dict(v, translate_func, target_name)
|
||||
return new_dict
|
||||
elif isinstance(data, list):
|
||||
return [translate_dict(item, translate_func, target_name) for item in data]
|
||||
elif isinstance(data, str):
|
||||
if not data.strip():
|
||||
return data
|
||||
|
||||
temp_text, vars_found = replace_vars_with_tokens(data)
|
||||
try:
|
||||
translated_temp = translate_func(temp_text)
|
||||
return restore_vars_from_tokens(translated_temp, vars_found)
|
||||
except Exception as e:
|
||||
print_warning(
|
||||
f"Failed to translate '{data}': {e}. Falling back to original."
|
||||
)
|
||||
return data
|
||||
else:
|
||||
return data
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Translate JSON localization files using Argos Translate."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--from", dest="from_lang", help="Source language code (e.g. 'en')"
|
||||
)
|
||||
parser.add_argument("--to", dest="to_lang", help="Target language code (e.g. 'zh')")
|
||||
parser.add_argument("--input", dest="input_file", help="Path to input JSON file")
|
||||
parser.add_argument("--output", dest="output_file", help="Path to output JSON file")
|
||||
parser.add_argument(
|
||||
"--name",
|
||||
dest="target_name",
|
||||
help="Native name of the target language (e.g. '中文' for Chinese)",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Fallback to Environment Variables if arguments aren't provided
|
||||
from_lang = args.from_lang or os.environ.get("ARGOS_FROM_LANG")
|
||||
to_lang = args.to_lang or os.environ.get("ARGOS_TO_LANG")
|
||||
input_file = args.input_file or os.environ.get("ARGOS_INPUT_FILE")
|
||||
output_file = args.output_file or os.environ.get("ARGOS_OUTPUT_FILE")
|
||||
target_name = args.target_name or os.environ.get("ARGOS_TARGET_NAME")
|
||||
|
||||
if not all([from_lang, to_lang, input_file, output_file]):
|
||||
parser.print_help()
|
||||
print_error(
|
||||
"Missing required arguments. Please provide --from, --to, --input, and --output."
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
if not os.path.exists(input_file):
|
||||
print_error(f"Input file not found: {input_file}")
|
||||
sys.exit(1)
|
||||
|
||||
print_info(f"Source Language: {Colors.BOLD}{from_lang}{Colors.ENDC}")
|
||||
print_info(f"Target Language: {Colors.BOLD}{to_lang}{Colors.ENDC}")
|
||||
print_info(f"Input File: {Colors.BOLD}{input_file}{Colors.ENDC}")
|
||||
print_info(f"Output File: {Colors.BOLD}{output_file}{Colors.ENDC}")
|
||||
|
||||
# Load JSON
|
||||
try:
|
||||
with open(input_file, "r", encoding="utf-8") as f:
|
||||
source_data = json.load(f)
|
||||
except json.JSONDecodeError as e:
|
||||
print_error(f"Invalid JSON in input file: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
start_time = time.time()
|
||||
|
||||
# Get Translator
|
||||
translate_func = get_translation_func(from_lang, to_lang)
|
||||
|
||||
print_info(
|
||||
"Starting translation. This may take a moment depending on the file size..."
|
||||
)
|
||||
translated_data = translate_dict(source_data, translate_func, target_name)
|
||||
|
||||
# Ensure output directory exists
|
||||
os.makedirs(os.path.dirname(os.path.abspath(output_file)), exist_ok=True)
|
||||
|
||||
# Save translated JSON
|
||||
try:
|
||||
with open(output_file, "w", encoding="utf-8") as f:
|
||||
json.dump(translated_data, f, ensure_ascii=False, indent=4)
|
||||
f.write("\n")
|
||||
except IOError as e:
|
||||
print_error(f"Could not write to output file: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
elapsed_time = time.time() - start_time
|
||||
print_success(f"Translation complete in {elapsed_time:.2f} seconds!")
|
||||
print_success(f"Output saved to {Colors.BOLD}{output_file}{Colors.ENDC}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -19,7 +19,7 @@ Options:
|
||||
--python-minor X.Y Python minor for target wheels (default: 3.11)
|
||||
--target-version V Explicit Chaquopy target version (default: auto latest for python minor)
|
||||
--chaquopy-ref REF Chaquopy git ref/commit to checkout (default: master)
|
||||
--abis LIST Comma-separated ABIs (default: arm64-v8a,x86_64)
|
||||
--abis LIST Comma-separated ABIs (default: arm64-v8a,x86_64,armeabi-v7a)
|
||||
--api-level N Android API level for wheel tag (default: 24)
|
||||
--pycodec2-version V pycodec2 version to build (default: 4.1.1)
|
||||
--numpy-version V NumPy version used during pycodec2 build (default: 1.26.2)
|
||||
@@ -36,7 +36,7 @@ ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
PYTHON_MINOR="3.11"
|
||||
TARGET_VERSION=""
|
||||
CHAQUOPY_REF="${CHAQUOPY_REF:-master}"
|
||||
ABI_LIST="arm64-v8a,x86_64"
|
||||
ABI_LIST="arm64-v8a,x86_64,armeabi-v7a"
|
||||
API_LEVEL="24"
|
||||
PYCODEC2_VERSION="4.1.1"
|
||||
LIBCODEC2_VERSION="1.2.0"
|
||||
@@ -145,10 +145,26 @@ require_cmd sed
|
||||
require_cmd awk
|
||||
require_cmd sort
|
||||
|
||||
PYTHON_BIN="python${PYTHON_MINOR}"
|
||||
case ",${ABI_LIST}," in
|
||||
*,armeabi-v7a,*)
|
||||
if [[ -z "${ANDROID_HOME:-}" && -z "${ANDROID_SDK_ROOT:-}" ]]; then
|
||||
echo "armeabi-v7a: NumPy is built from source and needs the Android SDK/NDK (Chaquopy android-env.sh)." >&2
|
||||
echo "Set ANDROID_HOME or ANDROID_SDK_ROOT to your SDK root (with cmdline-tools/latest/bin/sdkmanager)." >&2
|
||||
exit 1
|
||||
fi
|
||||
export ANDROID_HOME="${ANDROID_HOME:-${ANDROID_SDK_ROOT}}"
|
||||
export ANDROID_SDK_ROOT="${ANDROID_SDK_ROOT:-${ANDROID_HOME}}"
|
||||
if [[ ! -x "${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" ]]; then
|
||||
echo "armeabi-v7a: expected sdkmanager at ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
PYTHON_BIN="${PYTHON_BIN:-python${PYTHON_MINOR}}"
|
||||
if ! command -v "${PYTHON_BIN}" >/dev/null 2>&1; then
|
||||
echo "Required interpreter not found on PATH: ${PYTHON_BIN}" >&2
|
||||
echo "Install Python ${PYTHON_MINOR} locally before running this script." >&2
|
||||
echo "Required interpreter not found: ${PYTHON_BIN}" >&2
|
||||
echo "Install Python ${PYTHON_MINOR} (e.g. uv python install ${PYTHON_MINOR}) or set PYTHON_BIN." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -198,6 +214,28 @@ fi
|
||||
NUMPY_DIST_DIR="${PYPIDIR}/dist/numpy"
|
||||
mkdir -p "${NUMPY_DIST_DIR}"
|
||||
PYTHON_ABI_TAG="cp${PYTHON_MINOR/./}"
|
||||
|
||||
# Chaquopy's numpy recipe lists chaquopy-openblas as a host requirement. build-wheel.py
|
||||
# only loads it from ${PYPIDIR}/dist/chaquopy-openblas/ (it does not fetch from the index).
|
||||
# Official wheels: https://chaquo.com/pypi-13.1/chaquopy-openblas/
|
||||
cache_chaquopy_openblas_for_abi() {
|
||||
local abi="$1"
|
||||
local name url
|
||||
mkdir -p "${PYPIDIR}/dist/chaquopy-openblas"
|
||||
case "${abi}" in
|
||||
armeabi-v7a) name="chaquopy_openblas-0.2.20-5-py3-none-android_16_armeabi_v7a.whl" ;;
|
||||
arm64-v8a) name="chaquopy_openblas-0.2.20-5-py3-none-android_21_arm64_v8a.whl" ;;
|
||||
x86_64) name="chaquopy_openblas-0.2.20-5-py3-none-android_21_x86_64.whl" ;;
|
||||
*) return 0 ;;
|
||||
esac
|
||||
url="https://chaquo.com/pypi-13.1/chaquopy-openblas/${name}"
|
||||
if [[ -f "${PYPIDIR}/dist/chaquopy-openblas/${name}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
echo "Caching Chaquopy OpenBLAS wheel for ${abi}"
|
||||
curl -fsSL -o "${PYPIDIR}/dist/chaquopy-openblas/${name}" "${url}"
|
||||
}
|
||||
|
||||
for abi in ${ABI_LIST//,/ }; do
|
||||
platform_tag="$(abi_to_platform_tag "${abi}")"
|
||||
echo "Resolving NumPy wheel for ABI ${abi} (${platform_tag})"
|
||||
@@ -213,12 +251,12 @@ for abi in ${ABI_LIST//,/ }; do
|
||||
--extra-index-url https://chaquo.com/pypi-13.1 \
|
||||
--dest "${NUMPY_DIST_DIR}"; then
|
||||
echo "No prebuilt NumPy wheel for ${abi}; building locally via Chaquopy recipe"
|
||||
cache_chaquopy_openblas_for_abi "${abi}"
|
||||
"${VENV_DIR}/bin/python" "${PYPIDIR}/build-wheel.py" \
|
||||
--python "${PYTHON_MINOR}" \
|
||||
--api-level "${API_LEVEL}" \
|
||||
--abi "${abi}" \
|
||||
"${PYPIDIR}/packages/numpy"
|
||||
cp -f "${PYPIDIR}/dist/numpy"/numpy-"${NUMPY_VERSION}"-*.whl "${NUMPY_DIST_DIR}/"
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Sign Android release APKs (and optionally add SourceStamp).
|
||||
|
||||
Required env vars:
|
||||
SIGNING_KEYSTORE_PATH Path to signing keystore (.jks/.keystore)
|
||||
SIGNING_KEY_ALIAS Alias of signing key
|
||||
SIGNING_KEYSTORE_PASSWORD Keystore password
|
||||
SIGNING_KEY_PASSWORD Key password (defaults to SIGNING_KEYSTORE_PASSWORD)
|
||||
|
||||
Optional env vars:
|
||||
ANDROID_HOME Android SDK root (or ANDROID_SDK_ROOT)
|
||||
APK_GLOB Glob for unsigned APKs
|
||||
(default: android/app/build/outputs/apk/release/*-unsigned.apk)
|
||||
ENABLE_SOURCESTAMP true/false (default: false)
|
||||
SOURCESTAMP_KEYSTORE_PATH Path to SourceStamp keystore
|
||||
SOURCESTAMP_KEY_ALIAS Alias in SourceStamp keystore
|
||||
SOURCESTAMP_KEYSTORE_PASSWORD SourceStamp keystore password
|
||||
SOURCESTAMP_KEY_PASSWORD SourceStamp key password
|
||||
|
||||
Output:
|
||||
Creates sibling files for each unsigned APK:
|
||||
*-aligned.apk
|
||||
*-signed.apk
|
||||
|
||||
Examples:
|
||||
SIGNING_KEYSTORE_PATH=android/keystore/meshchatx-release.jks \
|
||||
SIGNING_KEY_ALIAS=meshchatx-release \
|
||||
SIGNING_KEYSTORE_PASSWORD='...' \
|
||||
SIGNING_KEY_PASSWORD='...' \
|
||||
bash scripts/sign-android-apks.sh
|
||||
|
||||
SIGNING_KEYSTORE_PATH=android/keystore/meshchatx-release.jks \
|
||||
SIGNING_KEY_ALIAS=meshchatx-release \
|
||||
SIGNING_KEYSTORE_PASSWORD='...' \
|
||||
SIGNING_KEY_PASSWORD='...' \
|
||||
ENABLE_SOURCESTAMP=true \
|
||||
SOURCESTAMP_KEYSTORE_PATH=android/keystore/meshchatx-stamp.jks \
|
||||
SOURCESTAMP_KEY_ALIAS=meshchatx-stamp \
|
||||
SOURCESTAMP_KEYSTORE_PASSWORD='...' \
|
||||
SOURCESTAMP_KEY_PASSWORD='...' \
|
||||
bash scripts/sign-android-apks.sh
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
require_env() {
|
||||
local name="$1"
|
||||
if [[ -z "${!name:-}" ]]; then
|
||||
echo "Missing required env var: ${name}" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
require_env SIGNING_KEYSTORE_PATH
|
||||
require_env SIGNING_KEY_ALIAS
|
||||
require_env SIGNING_KEYSTORE_PASSWORD
|
||||
SIGNING_KEY_PASSWORD="${SIGNING_KEY_PASSWORD:-${SIGNING_KEYSTORE_PASSWORD}}"
|
||||
|
||||
ANDROID_HOME="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
|
||||
if [[ -z "${ANDROID_HOME}" ]]; then
|
||||
echo "Set ANDROID_HOME (or ANDROID_SDK_ROOT) to your Android SDK path." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BT_DIR="$(ls -d "${ANDROID_HOME}"/build-tools/* 2>/dev/null | sort -V | tail -n 1)"
|
||||
if [[ -z "${BT_DIR}" ]]; then
|
||||
echo "No Android build-tools found under ${ANDROID_HOME}/build-tools." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -x "${BT_DIR}/zipalign" || ! -x "${BT_DIR}/apksigner" ]]; then
|
||||
echo "Missing zipalign/apksigner in ${BT_DIR}. Install build-tools via sdkmanager." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APK_GLOB="${APK_GLOB:-android/app/build/outputs/apk/release/*-unsigned.apk}"
|
||||
shopt -s nullglob
|
||||
APKS=( ${APK_GLOB} )
|
||||
shopt -u nullglob
|
||||
if [[ ${#APKS[@]} -eq 0 ]]; then
|
||||
echo "No unsigned APKs matched: ${APK_GLOB}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ENABLE_SOURCESTAMP="${ENABLE_SOURCESTAMP:-false}"
|
||||
if [[ "${ENABLE_SOURCESTAMP}" == "true" ]]; then
|
||||
require_env SOURCESTAMP_KEYSTORE_PATH
|
||||
require_env SOURCESTAMP_KEY_ALIAS
|
||||
require_env SOURCESTAMP_KEYSTORE_PASSWORD
|
||||
SOURCESTAMP_KEY_PASSWORD="${SOURCESTAMP_KEY_PASSWORD:-${SOURCESTAMP_KEYSTORE_PASSWORD}}"
|
||||
fi
|
||||
|
||||
for apk in "${APKS[@]}"; do
|
||||
base="${apk%-unsigned.apk}"
|
||||
aligned="${base}-aligned.apk"
|
||||
signed="${base}-signed.apk"
|
||||
|
||||
echo "Aligning ${apk}"
|
||||
"${BT_DIR}/zipalign" -p -f 4 "${apk}" "${aligned}"
|
||||
|
||||
sign_args=(
|
||||
sign
|
||||
--ks "${SIGNING_KEYSTORE_PATH}"
|
||||
--ks-key-alias "${SIGNING_KEY_ALIAS}"
|
||||
--ks-pass "pass:${SIGNING_KEYSTORE_PASSWORD}"
|
||||
--key-pass "pass:${SIGNING_KEY_PASSWORD}"
|
||||
--out "${signed}"
|
||||
)
|
||||
|
||||
if [[ "${ENABLE_SOURCESTAMP}" == "true" ]]; then
|
||||
sign_args+=(
|
||||
--stamp-signer
|
||||
--stamp-ks "${SOURCESTAMP_KEYSTORE_PATH}"
|
||||
--stamp-key-alias "${SOURCESTAMP_KEY_ALIAS}"
|
||||
--stamp-ks-pass "pass:${SOURCESTAMP_KEYSTORE_PASSWORD}"
|
||||
--stamp-key-pass "pass:${SOURCESTAMP_KEY_PASSWORD}"
|
||||
)
|
||||
fi
|
||||
|
||||
echo "Signing ${aligned}"
|
||||
"${BT_DIR}/apksigner" "${sign_args[@]}" "${aligned}"
|
||||
|
||||
echo "Verifying ${signed}"
|
||||
"${BT_DIR}/apksigner" verify --verbose --print-certs "${signed}"
|
||||
done
|
||||
|
||||
echo "Done."
|
||||
Reference in New Issue
Block a user