From 674a14a8fe447875d442b3f9c043c573389a3b08 Mon Sep 17 00:00:00 2001 From: Marcus Sanatan Date: Sat, 31 Jan 2026 09:20:57 -0400 Subject: [PATCH] Add manual scripts to update pypi and docker in case things go bad! --- tools/docker_publish.sh | 54 ++++++++++++++++++++++++++ tools/pypi_publish.sh | 86 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100755 tools/docker_publish.sh create mode 100755 tools/pypi_publish.sh diff --git a/tools/docker_publish.sh b/tools/docker_publish.sh new file mode 100755 index 0000000..55690bf --- /dev/null +++ b/tools/docker_publish.sh @@ -0,0 +1,54 @@ +# Publish a Docker image (manual). + # + # Requirements: + # - Docker installed with buildx support (e.g. Docker Desktop). + # - Authenticated to the target registry (e.g. run: docker login). + # - Run from the `tools/` directory (this script uses build context `.` and Dockerfile `../Server/Dockerfile`). + # + # Usage: + # ./docker_publish.sh + # IMAGE= ./docker_publish.sh + # + # Examples: + # ./docker_publish.sh msanatan/mcp-for-unity-server 9.3.1 + # IMAGE=msanatan/mcp-for-unity-server ./docker_publish.sh v9.3.1 + # + # Tags pushed: + # - vX.Y.Z + # - vX.Y + # - vX + set -euo pipefail + + if [[ "${1:-}" == "" || "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + echo "Usage: $(basename "$0") " >&2 + echo " $(basename "$0") # if IMAGE env var is set" >&2 + echo "Example: $(basename "$0") youruser/mcp-for-unity-server 1.2.3" >&2 + exit 2 + fi + + if [[ "${2:-}" != "" ]]; then + IMAGE="$1" + VERSION_RAW="$2" + else + if [[ "${IMAGE:-}" == "" ]]; then + echo "Error: IMAGE env var is required when calling with a single arg." >&2 + echo "Usage: $(basename "$0") " >&2 + exit 2 + fi + VERSION_RAW="$1" + fi + + VERSION="${VERSION_RAW#v}" + + MAJOR="${VERSION%%.*}" + MINOR="${VERSION%.*}" # leaves X.Y + # (works for X.Y.Z) + + docker buildx build \ + --platform linux/amd64 \ + -f ../Server/Dockerfile \ + -t "$IMAGE:v$VERSION" \ + -t "$IMAGE:v$MINOR" \ + -t "$IMAGE:v$MAJOR" \ + --push \ + . diff --git a/tools/pypi_publish.sh b/tools/pypi_publish.sh new file mode 100755 index 0000000..aee86ae --- /dev/null +++ b/tools/pypi_publish.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +# Build and upload the Python package to PyPI (manual). +# +# Requirements: +# - Python 3 available on PATH. +# - `uv` installed (used to build the sdist and wheel). +# - `twine` installed (used to upload to PyPI / TestPyPI). +# - Credentials provided via environment variables: +# - Preferred: PYPI_TOKEN (a PyPI API token) +# - Or: TWINE_USERNAME and TWINE_PASSWORD +# +# Usage: +# export PYPI_TOKEN="pypi-..." +# ./tools/pypi_publish.sh +# +# TestPyPI: +# ./tools/pypi_publish.sh --test +# +# Notes: +# - PyPI does not allow overwriting an existing version; bump the version in Server/pyproject.toml first. +# - This script clears Server/dist/*.whl and Server/dist/*.tar.gz before building. +# - Only artifacts matching the current version in Server/pyproject.toml are uploaded. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" + +REPOSITORY="pypi" + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + echo "Usage: $(basename "$0") [--test]" >&2 + echo "Environment:" >&2 + echo " PYPI_TOKEN (preferred) or TWINE_USERNAME/TWINE_PASSWORD" >&2 + exit 2 +fi + +if [[ "${1:-}" == "--test" ]]; then + REPOSITORY="testpypi" +fi + +if [[ "${PYPI_TOKEN:-}" != "" && "${TWINE_PASSWORD:-}" == "" ]]; then + export TWINE_USERNAME="__token__" + export TWINE_PASSWORD="$PYPI_TOKEN" +fi + +if [[ "${TWINE_USERNAME:-}" == "" || "${TWINE_PASSWORD:-}" == "" ]]; then + echo "Error: missing credentials. Set PYPI_TOKEN or TWINE_USERNAME and TWINE_PASSWORD." >&2 + exit 2 +fi + +if ! command -v uv >/dev/null 2>&1; then + echo "Error: uv is not installed. Install it and retry." >&2 + exit 2 +fi + +python3 -m twine --version >/dev/null 2>&1 || { + echo "Error: twine is not installed. Install it (e.g. python3 -m pip install --upgrade twine) and retry." >&2 + exit 2 +} + +( + cd "$ROOT_DIR/Server" + mkdir -p dist + rm -f dist/*.whl dist/*.tar.gz + uv build +) + +DIST_DIR="$ROOT_DIR/Server/dist" +if [[ ! -d "$DIST_DIR" ]]; then + echo "Error: dist dir not found: $DIST_DIR" >&2 + exit 2 +fi + +shopt -s nullglob +VERSION="$(python3 -c 'import tomllib, pathlib; p = pathlib.Path("'"$ROOT_DIR"'/Server/pyproject.toml"); print(tomllib.loads(p.read_text(encoding="utf-8"))["project"]["version"])')" +FILES=("$DIST_DIR"/mcpforunityserver-"$VERSION"*) +shopt -u nullglob + +if (( ${#FILES[@]} == 0 )); then + echo "Error: no files found in $DIST_DIR" >&2 + exit 2 +fi + +python3 -m twine upload --repository "$REPOSITORY" "${FILES[@]}"