mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-05-31 11:46:58 -04:00
Add refresh-external-dependencies.sh script (#2082)
This commit is contained in:
committed by
Leendert de Borst
parent
3cb8e8983c
commit
2dc6ed055d
224
scripts/refresh-external-dependencies.sh
Executable file
224
scripts/refresh-external-dependencies.sh
Executable file
@@ -0,0 +1,224 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Refresh local copies of external dependencies that we cache in the repo
|
||||
# (allowlists, public key bundles, etc.) so the mobile/browser/server apps
|
||||
# do not need to fetch them at runtime.
|
||||
#
|
||||
# Run as part of the manual release pipeline.
|
||||
#
|
||||
# Usage:
|
||||
# ./refresh-external-dependencies.sh # run every task
|
||||
# ./refresh-external-dependencies.sh <task>... # run only the named task(s)
|
||||
# ./refresh-external-dependencies.sh --list # show available tasks
|
||||
#
|
||||
# Adding a new task:
|
||||
# 1. Define a function named `task_<name>` that fetches the upstream payload
|
||||
# and writes the result into the relevant source file. Use the
|
||||
# `replace_generated_block` helper to swap content between
|
||||
# `// BEGIN_GENERATED: <id>` and `// END_GENERATED: <id>` markers (any
|
||||
# comment syntax works — the helper matches both line- and block-style).
|
||||
# 2. Register the task in TASK_ORDER + TASK_DESCRIPTIONS below.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ -z "${BASH_VERSION:-}" ]; then
|
||||
echo "Error: this script must be run with bash" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||
TODAY="$(date +%Y-%m-%d)"
|
||||
|
||||
# Colors
|
||||
BLUE='\033[0;34m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
RED='\033[0;31m'
|
||||
RESET='\033[0m'
|
||||
|
||||
WORK_DIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORK_DIR"' EXIT
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Helpers shared by all tasks
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Replace everything between `BEGIN_GENERATED: <id>` and `END_GENERATED: <id>`
|
||||
# markers in $1 with the contents of the file at $3. The marker lines
|
||||
# themselves are preserved; only the lines between them change.
|
||||
#
|
||||
# Arguments:
|
||||
# $1 target file
|
||||
# $2 block id (must match the marker)
|
||||
# $3 path to file whose contents become the new block body
|
||||
replace_generated_block() {
|
||||
local target_file="$1"
|
||||
local block_id="$2"
|
||||
local payload_file="$3"
|
||||
|
||||
python3 - "$target_file" "$block_id" "$payload_file" <<'PY'
|
||||
import re
|
||||
import sys
|
||||
|
||||
target, block_id, payload_path = sys.argv[1:4]
|
||||
|
||||
with open(target, "r") as f:
|
||||
src = f.read()
|
||||
with open(payload_path, "r") as f:
|
||||
payload = f.read().rstrip("\n")
|
||||
|
||||
pattern = re.compile(
|
||||
r"(BEGIN_GENERATED:\s*" + re.escape(block_id) + r".*?\n)"
|
||||
r"(.*?)"
|
||||
r"(^[ \t]*\S+\s*END_GENERATED:\s*" + re.escape(block_id) + r")",
|
||||
re.DOTALL | re.MULTILINE,
|
||||
)
|
||||
|
||||
match = pattern.search(src)
|
||||
if not match:
|
||||
sys.stderr.write(
|
||||
f"ERROR: BEGIN_GENERATED/END_GENERATED markers for '{block_id}' "
|
||||
f"not found in {target}\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
new_src = src[:match.start()] + match.group(1) + payload + "\n" + match.group(3) + src[match.end():]
|
||||
|
||||
with open(target, "w") as f:
|
||||
f.write(new_src)
|
||||
PY
|
||||
}
|
||||
|
||||
# Download a URL into a file, failing loudly on HTTP errors.
|
||||
download() {
|
||||
local url="$1"
|
||||
local out="$2"
|
||||
if ! curl --fail --silent --show-error --location "$url" -o "$out"; then
|
||||
echo -e " ${RED}✗${RESET} download failed: $url" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Validate that a file contains parseable JSON.
|
||||
ensure_valid_json() {
|
||||
local file="$1"
|
||||
if ! python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$file" 2>/dev/null; then
|
||||
echo -e " ${RED}✗${RESET} downloaded content is not valid JSON" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Tasks — one function per external dependency
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
task_passkeys_allowlist() {
|
||||
local url="https://www.gstatic.com/gpm-passkeys-privileged-apps/apps.json"
|
||||
local target="$REPO_ROOT/apps/mobile-app/android/app/src/main/java/net/aliasvault/app/credentialprovider/OriginVerifier.kt"
|
||||
local block_id="passkeys-allowlist"
|
||||
|
||||
echo -e " ${BLUE}↓${RESET} fetching $url"
|
||||
local raw="$WORK_DIR/passkeys-allowlist.json"
|
||||
download "$url" "$raw"
|
||||
ensure_valid_json "$raw"
|
||||
|
||||
# Build the replacement block. The JSON lines are embedded inside a
|
||||
# Kotlin triple-quoted string with `.trimIndent()`, so we prefix every
|
||||
# line with 8 spaces of indent — trimIndent strips that back out at
|
||||
# runtime, leaving the upstream bytes intact.
|
||||
local block="$WORK_DIR/passkeys-allowlist.block"
|
||||
{
|
||||
echo " // Source: $url"
|
||||
echo " // Last refreshed: $TODAY"
|
||||
echo ' private val PRIVILEGED_ALLOWLIST_JSON = """'
|
||||
awk '{ if (length($0) > 0) print " " $0; else print "" }' "$raw"
|
||||
echo ' """.trimIndent()'
|
||||
} > "$block"
|
||||
|
||||
replace_generated_block "$target" "$block_id" "$block"
|
||||
echo -e " ${GREEN}✓${RESET} updated ${target#$REPO_ROOT/}"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Registry
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# Each entry is "task-name:human description". The order here is the order
|
||||
# tasks run when no specific task argument is given.
|
||||
TASKS=(
|
||||
"passkeys-allowlist:Android privileged-apps allowlist for WebAuthn (gstatic.com/gpm-passkeys-privileged-apps/apps.json)"
|
||||
)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Dispatch
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
task_description() {
|
||||
local needle="$1"
|
||||
local entry
|
||||
for entry in "${TASKS[@]}"; do
|
||||
if [ "${entry%%:*}" = "$needle" ]; then
|
||||
echo "${entry#*:}"
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
list_tasks() {
|
||||
echo "Available tasks:"
|
||||
local entry name desc
|
||||
for entry in "${TASKS[@]}"; do
|
||||
name="${entry%%:*}"
|
||||
desc="${entry#*:}"
|
||||
printf " %-24s %s\n" "$name" "$desc"
|
||||
done
|
||||
}
|
||||
|
||||
run_task() {
|
||||
local name="$1"
|
||||
local fn="task_${name//-/_}"
|
||||
if ! declare -F "$fn" > /dev/null; then
|
||||
echo -e "${RED}Unknown task: $name${RESET}" >&2
|
||||
echo >&2
|
||||
list_tasks >&2
|
||||
return 1
|
||||
fi
|
||||
echo -e "${YELLOW}→${RESET} $name"
|
||||
"$fn"
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: $0 [options] [task...]
|
||||
|
||||
Refreshes local copies of external dependencies cached in this repo.
|
||||
With no arguments, runs every registered task in order.
|
||||
|
||||
Options:
|
||||
-l, --list Show available tasks and exit
|
||||
-h, --help Show this help and exit
|
||||
|
||||
EOF
|
||||
list_tasks
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
-h|--help|help)
|
||||
usage
|
||||
;;
|
||||
-l|--list)
|
||||
list_tasks
|
||||
;;
|
||||
"")
|
||||
for entry in "${TASKS[@]}"; do
|
||||
run_task "${entry%%:*}"
|
||||
done
|
||||
;;
|
||||
*)
|
||||
for arg in "$@"; do
|
||||
run_task "$arg"
|
||||
done
|
||||
;;
|
||||
esac
|
||||
Reference in New Issue
Block a user