Cloudflare's free plan limits request bodies to ~100MB, causing 413 on large docker layer pushes. Push through an SSH tunnel directly to the Gitea origin server on vps.git instead of through Cloudflare.
176 lines
5.1 KiB
Bash
Executable File
176 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
COMMAND=""
|
|
REGISTRY=""
|
|
IMAGE=""
|
|
TAG=""
|
|
PACKAGE_FILE=""
|
|
REPO=""
|
|
|
|
usage() {
|
|
echo "Usage:"
|
|
echo " publish-package docker <registry> <image> <tag> --proj <repo>"
|
|
echo " publish-package nuget <source> <package-file> --proj <repo>"
|
|
echo " publish-package pypi <package-file> --proj <repo>"
|
|
echo " publish-package npm --proj <repo>"
|
|
exit 1
|
|
}
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
docker|nuget|pypi|npm)
|
|
COMMAND="$1"
|
|
shift
|
|
;;
|
|
--proj)
|
|
REPO="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
if [[ -z "$COMMAND" ]]; then
|
|
usage
|
|
fi
|
|
case $COMMAND in
|
|
docker)
|
|
[[ -z "$REGISTRY" ]] && REGISTRY="$1" && shift && continue
|
|
[[ -z "$IMAGE" ]] && IMAGE="$1" && shift && continue
|
|
[[ -z "$TAG" ]] && TAG="$1" && shift && continue
|
|
;;
|
|
nuget)
|
|
[[ -z "$SOURCE" ]] && SOURCE="$1" && shift && continue
|
|
[[ -z "$PACKAGE_FILE" ]] && PACKAGE_FILE="$1" && shift && continue
|
|
;;
|
|
pypi)
|
|
[[ -z "$PACKAGE_FILE" ]] && PACKAGE_FILE="$1" && shift && continue
|
|
;;
|
|
esac
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ -z "$COMMAND" ]] || [[ -z "$REPO" ]]; then
|
|
usage
|
|
fi
|
|
|
|
do_docker() {
|
|
if [[ -z "$REGISTRY" ]] || [[ -z "$IMAGE" ]] || [[ -z "$TAG" ]]; then
|
|
echo "Error: docker requires <registry> <image> <tag>"
|
|
exit 1
|
|
fi
|
|
|
|
OWNER=$(ego-mgr get default-username)
|
|
if [[ -z "$OWNER" ]]; then
|
|
echo "Error: cannot get username from ego-mgr"
|
|
exit 1
|
|
fi
|
|
|
|
KEY=$(python3 -c "import uuid; print(uuid.uuid4())")
|
|
|
|
LOCKFILE="$HOME/.openclaw/.docker"
|
|
lock-mgr acquire "$LOCKFILE" "$KEY"
|
|
|
|
# Push via SSH tunnel to bypass Cloudflare's 100MB request body limit.
|
|
# Tunnel forwards 127.0.0.1:$TUNNEL_PORT on this host to Gitea's HTTP port on vps.git.
|
|
TUNNEL_HOST="root@vps.git"
|
|
TUNNEL_PORT="5000"
|
|
TUNNEL_LOCAL="127.0.0.1:${TUNNEL_PORT}"
|
|
TUNNEL_CTL="$HOME/.openclaw/.docker-tunnel.sock"
|
|
rm -f "$TUNNEL_CTL"
|
|
ssh -fN -o ExitOnForwardFailure=yes -o ControlMaster=yes -o ControlPath="$TUNNEL_CTL" \
|
|
-L "${TUNNEL_LOCAL}:127.0.0.1:3000" "$TUNNEL_HOST"
|
|
|
|
cleanup() {
|
|
ssh -O exit -o ControlPath="$TUNNEL_CTL" "$TUNNEL_HOST" 2>/dev/null || true
|
|
rm -f "$TUNNEL_CTL"
|
|
docker logout "$REGISTRY" 2>/dev/null || true
|
|
docker logout "$TUNNEL_LOCAL" 2>/dev/null || true
|
|
lock-mgr release "$LOCKFILE" "$KEY" 2>/dev/null || true
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
echo "Logging in to $REGISTRY (via tunnel $TUNNEL_LOCAL)..."
|
|
docker login "$TUNNEL_LOCAL" -u "$OWNER" -p "$(secret-mgr get-secret --key git)" 2>/dev/null
|
|
|
|
FULL_IMAGE="${REGISTRY}/${OWNER}/${IMAGE}:${TAG}"
|
|
TUNNEL_IMAGE="${TUNNEL_LOCAL}/${OWNER}/${IMAGE}:${TAG}"
|
|
echo "Building: $FULL_IMAGE"
|
|
|
|
cd "$REPO"
|
|
docker build -t "$FULL_IMAGE" .
|
|
docker tag "$FULL_IMAGE" "$TUNNEL_IMAGE"
|
|
|
|
echo "Pushing via tunnel: $TUNNEL_IMAGE"
|
|
docker push "$TUNNEL_IMAGE"
|
|
docker rmi "$TUNNEL_IMAGE" 2>/dev/null || true
|
|
|
|
# Link package to repository
|
|
TOKEN=$(secret-mgr get-secret --key git-access-token)
|
|
REPO_NAME=$(basename "$REPO")
|
|
|
|
# Determine the actual owner of the git repository
|
|
REPO_OWNER="$OWNER"
|
|
if [[ -d "$REPO/.git" ]]; then
|
|
REMOTE_URL=$(git -C "$REPO" remote get-url origin 2>/dev/null || true)
|
|
if [[ "$REMOTE_URL" =~ git\.hangman-lab\.top/([^/]+)/ ]]; then
|
|
REPO_OWNER="${BASH_REMATCH[1]}"
|
|
fi
|
|
fi
|
|
|
|
LINK_RESP=$(curl -s -w "%{http_code}" -X POST \
|
|
-u "${OWNER}:${TOKEN}" \
|
|
"https://git.hangman-lab.top/api/v1/packages/${OWNER}/container/${IMAGE}/-/link/${REPO_NAME}")
|
|
LINK_STATUS="${LINK_RESP: -3}"
|
|
LINK_BODY="${LINK_RESP:0:-3}"
|
|
|
|
if [[ "$LINK_STATUS" != "200" && "$LINK_STATUS" != "201" ]]; then
|
|
if echo "$LINK_BODY" | grep -q '"message".*repository does not exist'; then
|
|
echo "Warning: repository '$REPO_NAME' is not owned by '$OWNER' — skipping link (requires site admin or matching owner)."
|
|
else
|
|
echo "Warning: package link failed (HTTP $LINK_STATUS): $LINK_BODY"
|
|
fi
|
|
fi
|
|
|
|
lock-mgr release "$LOCKFILE" "$KEY"
|
|
echo "Done: $FULL_IMAGE"
|
|
}
|
|
|
|
do_nuget() {
|
|
echo "publish-package nuget: not yet implemented"
|
|
exit 1
|
|
}
|
|
|
|
do_pypi() {
|
|
echo "publish-package pypi: not yet implemented"
|
|
exit 1
|
|
}
|
|
|
|
do_npm() {
|
|
echo "publish-package npm: not yet implemented"
|
|
exit 1
|
|
}
|
|
|
|
# For docker, determine the actual repo owner via search API and switch to that owner's agent-id
|
|
if [[ "$COMMAND" == "docker" ]]; then
|
|
REPO_NAME=$(basename "$REPO")
|
|
SCRIPT_DIR_CALLER=$(cd "$(dirname "$0")" && pwd)
|
|
search_result=$("$SCRIPT_DIR_CALLER/repo" search "$REPO_NAME" 2>&1) || true
|
|
if [[ -n "$search_result" ]] && echo "$search_result" | python3 -q -c "import sys,json; sys.exit(0 if json.load(sys.stdin) else 1)" 2>/dev/null; then
|
|
repo_owner=$(echo "$search_result" | python3 -c "import sys,json; print(json.load(sys.stdin).get('owner',''))" 2>/dev/null)
|
|
if [[ -n "$repo_owner" ]]; then
|
|
owner_agent_id=$(ego-mgr lookup "$repo_owner" 2>/dev/null || echo "")
|
|
if [[ -n "$owner_agent_id" ]]; then
|
|
export AGENT_ID="$owner_agent_id"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
case "$COMMAND" in
|
|
docker) do_docker ;;
|
|
nuget) do_nuget ;;
|
|
pypi) do_pypi ;;
|
|
npm) do_npm ;;
|
|
esac
|