Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions .github/actions/publish-docker/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Publish Docker image
description: Build and push the Docker image to Docker Hub
inputs:
docker_username:
required: true
description: Docker Hub username
docker_password:
required: true
description: Docker Hub password
image:
required: true
description: Docker image name (e.g. user/repo)
version:
required: false
default: ""
description: Optional version string (e.g. 1.2.3). If provided, tags are computed from this version instead of the GitHub ref.
include_branch_tags:
required: false
default: "true"
description: Whether to also publish a branch tag when running on a branch ref (e.g. manual runs).
context:
required: false
default: .
description: Docker build context
dockerfile:
required: false
default: Server/Dockerfile
description: Path to Dockerfile
platforms:
required: false
default: linux/amd64
description: Target platforms
runs:
using: composite
steps:
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ inputs.docker_username }}
password: ${{ inputs.docker_password }}

- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
if: ${{ inputs.version == '' && inputs.include_branch_tags == 'true' }}
with:
images: ${{ inputs.image }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=ref,event=branch

- name: Extract metadata (tags, labels) for Docker
id: meta_nobranch
uses: docker/metadata-action@v5
if: ${{ inputs.version == '' && inputs.include_branch_tags != 'true' }}
with:
images: ${{ inputs.image }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}

- name: Compute Docker tags from version
id: version_tags
if: ${{ inputs.version != '' }}
shell: bash
run: |
set -euo pipefail
IFS='.' read -r MA MI PA <<< "${{ inputs.version }}"
echo "major=$MA" >> "$GITHUB_OUTPUT"
echo "minor=$MI" >> "$GITHUB_OUTPUT"

- name: Extract metadata (tags, labels) for Docker
id: meta_version
uses: docker/metadata-action@v5
if: ${{ inputs.version != '' && inputs.include_branch_tags == 'true' }}
with:
images: ${{ inputs.image }}
tags: |
type=raw,value=v${{ inputs.version }}
type=raw,value=v${{ steps.version_tags.outputs.major }}.${{ steps.version_tags.outputs.minor }}
type=raw,value=v${{ steps.version_tags.outputs.major }}
type=ref,event=branch

- name: Extract metadata (tags, labels) for Docker
id: meta_version_nobranch
uses: docker/metadata-action@v5
if: ${{ inputs.version != '' && inputs.include_branch_tags != 'true' }}
with:
images: ${{ inputs.image }}
tags: |
type=raw,value=v${{ inputs.version }}
type=raw,value=v${{ steps.version_tags.outputs.major }}.${{ steps.version_tags.outputs.minor }}
type=raw,value=v${{ steps.version_tags.outputs.major }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build and push Docker image
uses: docker/build-push-action@v6
with:
context: ${{ inputs.context }}
file: ${{ inputs.dockerfile }}
platforms: ${{ inputs.platforms }}
push: true
tags: ${{ steps.meta.outputs.tags || steps.meta_nobranch.outputs.tags || steps.meta_version.outputs.tags || steps.meta_version_nobranch.outputs.tags }}
labels: ${{ steps.meta.outputs.labels || steps.meta_nobranch.outputs.labels || steps.meta_version.outputs.labels || steps.meta_version_nobranch.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
21 changes: 21 additions & 0 deletions .github/actions/publish-pypi/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
name: Publish Python distribution to PyPI
description: Build and publish the Python package from Server/ to PyPI
runs:
using: composite
steps:
- name: Install uv
uses: astral-sh/setup-uv@v7
with:
version: "latest"
enable-cache: true
cache-dependency-glob: "Server/uv.lock"
Comment on lines +6 to +11
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

astral-sh setup-uv latest stable version best practices

💡 Result:

Short answer

  • Action major: use astral-sh/setup-uv@v7 (current stable major). [1][2]
  • uv binary: pin the uv release you need (e.g., version: "0.9.21") rather than relying on latest. [2][3]

Best-practice checklist (concise)

  1. Pin the action major and pin uv:
    • uses: astral-sh/setup-uv@v7
    • with: version: "0.9.21" (or the exact uv release you require). [1][2][3]
  2. Run actions/checkout before setup-uv when you need repo files (pyproject.toml / uv.toml). [1]
  3. Use working-directory if your project lives in a subfolder so version files are discovered there. [1]
  4. Enable caching and include resolution strategy in the cache key (or use the action’s caching guidance) to avoid cache conflicts. Add cache-suffix/resolution-strategy to cache keys. [1]
  5. Set python-version or run uv python install explicitly (don’t assume actions/setup-python unless you prefer it for speed). [1]
  6. Use the action outputs (uv-version, uv-path) when subsequent steps need the installed version. [1]
  7. (Optional) Verify checksum input for supply-chain safety and set github-token for rate limits when needed. [1]
  8. Persist or copy uv into Docker images by pinning the distroless image tag (e.g., ghcr.io/astral-sh/uv:0.9.21) for reproducible builds. [3]

Minimal example

  • uses: actions/checkout@v5
  • name: Install uv
    uses: astral-sh/setup-uv@v7
    with:
    version: "0.9.21"
    python-version: "3.12"
    enable-cache: true

Sources
[1] astral-sh/setup-uv (GitHub README).
[2] Using uv in GitHub Actions — docs.astral.sh/uv/guides/integration/github.
[3] Using uv in Docker — docs.astral.sh/uv/guides/integration/docker.


🌐 Web query:

uv Python package manager latest stable release version 2025

💡 Result:

The latest stable UV release in 2025 is version 0.9.21 (released Dec 30, 2025). [1][2]

Sources:
[1] PyPI — uv release history (uv‑0.9.21 uploaded Dec 30, 2025).
[2] GitHub — astral‑sh/uv releases (0.9.20 notes; repo releases list).


🌐 Web query:

astral-sh setup-uv action breaking changes version pinning recommendations

💡 Result:

Short summary of recent breaking changes and pinning recommendations for astral-sh/setup-uv

Breaking changes to be aware of

  • setup-uv v7: switched runtime from Node 20 → Node 24 and removed the deprecated server-url input (breaking for out-of-date self-hosted runners or workflows relying on server-url). [1]
  • uv 0.9.x (upstream) made Python 3.14 the default stable Python and noted potential breaking behavior if you do not pin Python versions. (This is an upstream uv release note you should consider when installing uv in CI.) [2]

Recommended version-pinning and workflow practices

  1. Pin the action major tag (and optionally patch) — e.g. uses: astral-sh/setup-uv@v7 — to avoid unexpected breaking changes from new major releases. [1]
  2. Pin the uv binary version explicitly in the action inputs: with: version: "0.9.18" (or whatever exact uv release you need). This prevents automatic upgrades when the action installs the “latest”. [3][1]
  3. Pin the Python version you want uv to use:
    • Either set with: python-version: "3.13" (or desired) in setup-uv, or
    • Use actions/setup-python before setup-uv and set python-version-file / python-version explicitly. This avoids surprising default upgrades (e.g., to Python 3.14). [1][2]
  4. Use a version-file (uv.toml or pyproject.toml) or the action input version-file to centralize the pinned uv version; set resolution-strategy if using ranges. [1]
  5. If you rely on caching or self-hosted runners, ensure runners/tools meet v7’s Node 24 requirement; update self-hosted runners if necessary. [1][3]
  6. For safety in CI: use the action output uv-version to assert the expected installed version in later steps, and enable checksum/manifest options if you download from custom sources. [1][3]

Useful references

  • Using uv with GitHub Actions / docs (examples, version-file, python-version): astral docs. [1]
  • setup-uv Marketplace / inputs & behavior. [3]
  • setup-uv releases (v7 breaking notes). [3]
  • uv releases (upstream breaking change: Python 3.14 default). [2]

If you want, I can draft the minimal workflow snippet showing recommended pinning (action + uv version + python-version + assertion).


Pin uv to a specific version (e.g., 0.9.21) instead of "latest" for reproducibility.

Using version: "latest" can lead to unexpected build failures when uv releases introduce breaking changes. Recent versions have introduced breaking behavior (e.g., uv 0.9.x changed the default Python version to 3.14). Pin to an exact version like version: "0.9.21" to ensure consistent and predictable builds, aligning with your stability requirements.

🤖 Prompt for AI Agents
In @.github/actions/publish-pypi/action.yml around lines 6 - 11, The workflow
step installing uv uses version: "latest", which risks breaking changes; change
the Install uv step (uses: astral-sh/setup-uv@v7) to pin a specific version by
replacing version: "latest" with an explicit pinned version string (e.g.,
version: "0.9.21") so builds are reproducible and avoid unexpected uv upgrades.


- name: Build a binary wheel and a source tarball
shell: bash
run: uv build
working-directory: ./Server

- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: Server/dist/
47 changes: 0 additions & 47 deletions .github/workflows/publish-docker.yml

This file was deleted.

55 changes: 0 additions & 55 deletions .github/workflows/publish-pypi.yml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Bump Version
name: Release

on:
workflow_dispatch:
Expand All @@ -15,13 +15,16 @@ on:

jobs:
bump:
name: "Bump version and tag"
name: Bump version, tag, and create release
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
new_version: ${{ steps.compute.outputs.new_version }}
tag: ${{ steps.tag.outputs.tag }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0

Expand Down Expand Up @@ -56,6 +59,15 @@ jobs:
echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT"
echo "current_version=$CURRENT_VERSION" >> "$GITHUB_OUTPUT"

- name: Compute tag
id: tag
env:
NEW_VERSION: ${{ steps.compute.outputs.new_version }}
shell: bash
run: |
set -euo pipefail
echo "tag=v${NEW_VERSION}" >> "$GITHUB_OUTPUT"

- name: Update files to new version
env:
NEW_VERSION: ${{ steps.compute.outputs.new_version }}
Expand Down Expand Up @@ -94,31 +106,69 @@ jobs:

- name: Create and push tag
env:
NEW_VERSION: ${{ steps.compute.outputs.new_version }}
TAG: ${{ steps.tag.outputs.tag }}
shell: bash
run: |
set -euo pipefail
TAG="v${NEW_VERSION}"
echo "Preparing to create tag $TAG"

if git ls-remote --tags origin | grep -q "refs/tags/$TAG$"; then
echo "Tag $TAG already exists on remote. Skipping tag creation."
exit 0
fi

git tag -a "$TAG" -m "Version ${NEW_VERSION}"
git tag -a "$TAG" -m "Version ${TAG#v}"
git push origin "$TAG"

- name: Create GitHub release
env:
NEW_VERSION: ${{ steps.compute.outputs.new_version }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
set -euo pipefail
TAG="v${NEW_VERSION}"
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.tag }}
name: ${{ steps.tag.outputs.tag }}
generate_release_notes: true

publish_docker:
name: Publish Docker image
needs:
- bump
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Check out the repo
uses: actions/checkout@v6
with:
ref: ${{ needs.bump.outputs.tag }}
fetch-depth: 0

- name: Build and push Docker image
uses: ./.github/actions/publish-docker
with:
docker_username: ${{ secrets.DOCKER_USERNAME }}
docker_password: ${{ secrets.DOCKER_PASSWORD }}
image: ${{ secrets.DOCKER_USERNAME }}/mcp-for-unity-server
version: ${{ needs.bump.outputs.new_version }}
include_branch_tags: "false"
context: .
dockerfile: Server/Dockerfile
platforms: linux/amd64

publish_pypi:
name: Publish Python distribution to PyPI
needs:
- bump
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/mcpforunityserver
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v6
with:
ref: ${{ needs.bump.outputs.tag }}
fetch-depth: 0

# Create release with auto-generated notes
gh release create "$TAG" \
--title "v${NEW_VERSION}" \
--generate-notes
- name: Build and publish
uses: ./.github/actions/publish-pypi