221 lines
6.1 KiB
Bash
Executable File
221 lines
6.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
COPY_DEST=/path/or/container:/dir \
|
|
copy_artifact.sh FILE [FILE ...]
|
|
COPY_SCRIPT=.pouch/builder/publish-apk.sh \
|
|
copy_artifact.sh FILE [FILE ...]
|
|
|
|
Copy built artifacts using COPY_SCRIPT when configured; otherwise copy to
|
|
COPY_DEST. COPY_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 Optional fallback. Local dir or container:/dir
|
|
COPY_SCRIPT Optional .pouch/builder/ relative path or absolute
|
|
executable. Receives FILE paths as positional args.
|
|
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" && -z "${COPY_SCRIPT:-}" || $# -eq 0 ]]; then
|
|
if [[ $# -eq 0 ]]; then
|
|
echo "Error: at least one file is required." >&2
|
|
else
|
|
echo "Error: set COPY_DEST or COPY_SCRIPT." >&2
|
|
fi
|
|
echo "Set the value 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
|
|
# Refresh Git's cached stat data after build tools rewrite tracked files.
|
|
git -C "$project_root" update-index -q --refresh 2>/dev/null || true
|
|
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
|
|
|
|
if [[ -n "${COPY_SCRIPT:-}" ]]; then
|
|
copy_script=$COPY_SCRIPT
|
|
if [[ "$copy_script" != /* ]]; then
|
|
relative_script=${copy_script#./}
|
|
if [[ "$relative_script" != .pouch/builder/* || -z "$project_root" ]]; then
|
|
echo "Error: relative COPY_SCRIPT must be under .pouch/builder/." >&2
|
|
exit 2
|
|
fi
|
|
copy_script="$project_root/$relative_script"
|
|
fi
|
|
if [[ ! -f "$copy_script" ]]; then
|
|
echo "Error: COPY_SCRIPT is not a file: $copy_script" >&2
|
|
exit 2
|
|
fi
|
|
if [[ ! -x "$copy_script" ]]; then
|
|
echo "Error: COPY_SCRIPT is not executable: $copy_script" >&2
|
|
exit 2
|
|
fi
|
|
artifact_args=()
|
|
for src in "$@"; do
|
|
if [[ ! -f "$src" ]]; then
|
|
echo "Error: not a file: $src" >&2
|
|
exit 2
|
|
fi
|
|
if [[ "$src" == /* ]]; then
|
|
artifact_args+=("$src")
|
|
else
|
|
artifact_args+=("$PWD/$src")
|
|
fi
|
|
done
|
|
if $dry_run; then
|
|
printf 'custom copy script:'
|
|
printf ' %q' "$copy_script" "${artifact_args[@]}"
|
|
printf '\n'
|
|
exit 0
|
|
fi
|
|
export BUILDER_PROJECT_ROOT=${project_root:-}
|
|
if "$copy_script" "${artifact_args[@]}"; then
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
for src in "${artifact_args[@]}"; do
|
|
sha256sum -- "$src"
|
|
done
|
|
fi
|
|
echo "copy_artifact: custom script completed for $# file(s)"
|
|
exit 0
|
|
else
|
|
status=$?
|
|
echo "Error: COPY_SCRIPT failed with exit code $status." >&2
|
|
exit "$status"
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$dest" ]]; then
|
|
echo "Error: COPY_DEST is required when COPY_SCRIPT is not configured." >&2
|
|
exit 2
|
|
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"
|