feat(builder): Android/iOS 构建与 copy_artifact 发布
新增 APK/IPA 轨道和 copy_artifact.sh(本地目录或 container:/dir)。 push 必须委托该脚本,禁止在 makefile 里内联 docker cp。
This commit is contained in:
Executable
+152
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
COPY_DEST=/path/or/container:/dir \
|
||||
copy_artifact.sh FILE [FILE ...]
|
||||
|
||||
Copy built artifacts to COPY_DEST. Dest is a local directory, or a Docker
|
||||
copy target `container:/absolute/dir` (docker cp). Does not read `.env`.
|
||||
|
||||
Environment (project root .env.builder; shell values win):
|
||||
COPY_DEST Required. Local dir or container:/dir
|
||||
COPY_AS Optional. Extra basenames for the first file
|
||||
(comma or space separated)
|
||||
ALLOW_UNCOMMITTED=1 Publish despite a dirty working tree
|
||||
|
||||
Options:
|
||||
-d DEST Override COPY_DEST
|
||||
-a NAMES Override COPY_AS
|
||||
--dry-run Print the copies without executing
|
||||
-h Show help
|
||||
|
||||
The working tree must be clean to publish; set ALLOW_UNCOMMITTED=1 to override.
|
||||
EOF
|
||||
}
|
||||
|
||||
project_root=$(git rev-parse --show-toplevel 2>/dev/null || true)
|
||||
|
||||
if [[ -n "$project_root" && -f "$project_root/.env.builder" ]]; then
|
||||
while IFS='=' read -r key value; do
|
||||
key=${key%%[[:space:]]*}
|
||||
[[ -z "$key" || "$key" == \#* ]] && continue
|
||||
if [[ -n "${!key:-}" ]]; then
|
||||
continue
|
||||
fi
|
||||
value=${value%\"}; value=${value#\"}; value=${value%\'}; value=${value#\'}
|
||||
[[ -z "$value" ]] && continue
|
||||
printf -v "$key" '%s' "$value"
|
||||
export "$key"
|
||||
done < <(grep -v '^[[:space:]]*$' "$project_root/.env.builder")
|
||||
fi
|
||||
|
||||
dest=${COPY_DEST:-}
|
||||
extra_as=${COPY_AS:-}
|
||||
dry_run=false
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
-d) dest=$2; shift 2 ;;
|
||||
-a) extra_as=$2; shift 2 ;;
|
||||
--dry-run) dry_run=true; shift ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
--) shift; break ;;
|
||||
-*) echo "Error: unknown option: $1" >&2; usage >&2; exit 2 ;;
|
||||
*) break ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z "$dest" || $# -eq 0 ]]; then
|
||||
echo "Error: COPY_DEST and at least one file are required." >&2
|
||||
echo "Set them in the environment or the project root .env.builder." >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ -n "$project_root" ]] && git -C "$project_root" rev-parse HEAD >/dev/null 2>&1; then
|
||||
if [[ ${ALLOW_UNCOMMITTED:-0} != 1 ]] && ! git -C "$project_root" diff-index --quiet HEAD -- 2>/dev/null; then
|
||||
echo "Error: working tree has uncommitted changes; refusing to publish." >&2
|
||||
echo "Commit first, or set ALLOW_UNCOMMITTED=1 to publish anyway." >&2
|
||||
exit 3
|
||||
fi
|
||||
fi
|
||||
|
||||
is_docker_dest() {
|
||||
local d=$1
|
||||
[[ "$d" != /* && "$d" != ./* && "$d" != ../* && "$d" != ~* ]] \
|
||||
&& [[ "$d" != *://* ]] \
|
||||
&& [[ "$d" =~ ^[A-Za-z0-9][A-Za-z0-9_.-]*:/ ]]
|
||||
}
|
||||
|
||||
docker_container=
|
||||
docker_dir=
|
||||
local_dir=
|
||||
if is_docker_dest "$dest"; then
|
||||
docker_container=${dest%%:*}
|
||||
docker_dir=${dest#*:}
|
||||
if [[ -z "$docker_container" || -z "$docker_dir" || "$docker_dir" != /* ]]; then
|
||||
echo "Error: docker dest must be container:/absolute/dir" >&2
|
||||
exit 2
|
||||
fi
|
||||
docker_dir=${docker_dir%/}
|
||||
else
|
||||
local_dir=${dest%/}
|
||||
fi
|
||||
|
||||
split_as() {
|
||||
local raw=$1
|
||||
raw=${raw//,/ }
|
||||
# shellcheck disable=SC2086
|
||||
printf '%s\n' $raw
|
||||
}
|
||||
|
||||
copy_one() {
|
||||
local src=$1 name=$2
|
||||
if [[ ! -f "$src" ]]; then
|
||||
echo "Error: not a file: $src" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ -n "$docker_container" ]]; then
|
||||
local target="${docker_container}:${docker_dir}/${name}"
|
||||
if $dry_run; then
|
||||
echo "docker cp $src $target"
|
||||
return
|
||||
fi
|
||||
docker cp "$src" "$target"
|
||||
docker exec "$docker_container" ls -l "${docker_dir}/${name}" >/dev/null
|
||||
echo "copied $(basename -- "$src") -> $target"
|
||||
else
|
||||
local target="${local_dir}/${name}"
|
||||
if $dry_run; then
|
||||
echo "cp $src $target"
|
||||
return
|
||||
fi
|
||||
mkdir -p "$local_dir"
|
||||
cp -a "$src" "$target"
|
||||
test -f "$target"
|
||||
echo "copied $(basename -- "$src") -> $target"
|
||||
fi
|
||||
}
|
||||
|
||||
first=true
|
||||
success=0
|
||||
for src in "$@"; do
|
||||
copy_one "$src" "$(basename -- "$src")"
|
||||
if $first; then
|
||||
first=false
|
||||
if [[ -n "$extra_as" ]]; then
|
||||
while IFS= read -r alias; do
|
||||
[[ -z "$alias" ]] && continue
|
||||
copy_one "$src" "$alias"
|
||||
done < <(split_as "$extra_as")
|
||||
fi
|
||||
fi
|
||||
if ! $dry_run && command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum -- "$src"
|
||||
fi
|
||||
success=$((success + 1))
|
||||
done
|
||||
|
||||
echo "copy_artifact: $success file(s) to $dest"
|
||||
Reference in New Issue
Block a user