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
13 changes: 13 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "tower-cli",
"owner": {
"name": "Tower"
},
"plugins": [
{
"name": "tower",
"source": "./plugin",
"description": "Tower compute platform — run and deploy Python apps, pipelines, and AI agents"
}
]
}
144 changes: 52 additions & 92 deletions .github/workflows/build-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,7 @@ jobs:
runs-on: macos-15
strategy:
matrix:
# Python 3.9/3.10 macOS builds require libintl from Homebrew gettext,
# which is not available on macos-15 runners
python-version: ["3.11", "3.12", "3.13", "3.14"]
python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -98,12 +96,12 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
architecture: x64
- name: "Build wheels - x86_64"
- name: Build wheel
uses: PyO3/maturin-action@v1.50.1
with:
target: x86_64
args: --release --locked --out dist -i python${{ matrix.python-version }}
- name: "Upload wheels"
- name: Upload wheel
uses: actions/upload-artifact@v6
with:
name: wheels-macos-x86_64-py${{ matrix.python-version }}
Expand All @@ -114,9 +112,7 @@ jobs:
runs-on: macos-15
strategy:
matrix:
# Python 3.9/3.10 macOS builds require libintl from Homebrew gettext,
# which is not available on macos-15 runners
python-version: ["3.11", "3.12", "3.13", "3.14"]
python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -145,17 +141,17 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
architecture: arm64
- name: "Build wheels - aarch64"
- name: Build wheel
uses: PyO3/maturin-action@v1.50.1
with:
target: aarch64
args: --release --locked --out dist -i python${{ matrix.python-version }}
- name: "Test wheel - aarch64"
- name: Test wheel
run: |
pip install dist/*.whl --force-reinstall
${{ env.EXECUTABLE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
- name: "Upload wheels"
- name: Upload wheel
uses: actions/upload-artifact@v6
with:
name: wheels-aarch64-apple-darwin-py${{ matrix.python-version }}
Expand All @@ -169,10 +165,7 @@ jobs:
platform:
- target: x86_64-pc-windows-msvc
arch: x64
# NOTE: i686 disabled due to poor support in PyArrow and friends.
#- target: i686-pc-windows-msvc
# arch: x86
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
with:
Expand All @@ -182,101 +175,63 @@ jobs:
with:
python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.platform.arch }}
- name: "Build wheels"
- name: Build wheel
uses: PyO3/maturin-action@v1.50.1
with:
target: ${{ matrix.platform.target }}
args: --release --locked --out dist -i python${{ matrix.python-version }}
- name: "Test wheel"
- name: Test wheel
shell: bash
run: |
python -m pip install dist/*.whl --force-reinstall
${{ env.EXECUTABLE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
- name: "Upload wheels"
- name: Upload wheel
uses: actions/upload-artifact@v6
with:
name: wheels-${{ matrix.platform.target }}-py${{ matrix.python-version }}
path: dist

windows-aarch64:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
runs-on: windows-11-arm
strategy:
matrix:
# Python ARM64 Windows builds started with 3.11
python-version: ["3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
- uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: "Build wheels"
uses: PyO3/maturin-action@v1.50.1
with:
target: aarch64-pc-windows-msvc
args: --release --locked --out dist -i python${{ matrix.python-version }}
- name: "Test wheel"
shell: bash
run: |
python -m pip install dist/*.whl --force-reinstall
${{ env.EXECUTABLE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
- name: "Upload wheels"
uses: actions/upload-artifact@v6
with:
name: wheels-aarch64-pc-windows-msvc-py${{ matrix.python-version }}
path: dist

linux:
if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-build') }}
runs-on: ubuntu-latest
strategy:
matrix:
target:
- x86_64-unknown-linux-gnu
- i686-unknown-linux-gnu
python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
- uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
python-version: ${{ matrix.python-version }}
architecture: x64
- name: "Build wheels"
- name: Build wheel
uses: PyO3/maturin-action@v1.50.1
with:
target: ${{ matrix.target }}
manylinux: auto
args: --release --locked --out dist -i python3.10 python3.11 python3.12 python3.13 python3.14
args: --release --locked --out dist -i python${{ matrix.python-version }}
before-script-linux: |
# If we're running on rhel centos, install needed packages.
if command -v yum &> /dev/null; then
yum update -y && yum install -y perl-core openssl openssl-devel pkgconfig libatomic

# If we're running on i686 we need to symlink libatomic
# in order to build openssl with -latomic flag.
if [[ ! -d "/usr/lib64" ]]; then
ln -s /usr/lib/libatomic.so.1 /usr/lib/libatomic.so
fi
else
# If we're running on debian-based system.
sudo apt update -y
sudo apt-get install -y libssl-dev openssl pkg-config
fi
- name: "Test wheel"
- name: Test wheel
if: ${{ startsWith(matrix.target, 'x86_64') }}
run: |
PYTAG="cp$(python -c "import sys; print(f'{sys.version_info.major}{sys.version_info.minor}')")"
pip install dist/*-${PYTAG}-*.whl --force-reinstall
${{ env.EXECUTABLE_NAME }} --help
python -m ${{ env.MODULE_NAME }} --help
- name: "Upload wheels"
- name: Upload wheel
uses: actions/upload-artifact@v6
with:
name: wheels-${{ matrix.target }}
Expand All @@ -293,49 +248,53 @@ jobs:
# see https://github.com/astral-sh/ruff/issues/3791
# and https://github.com/gnzlbg/jemallocator/issues/170#issuecomment-1503228963
maturin_docker_options: -e JEMALLOC_SYS_WITH_LG_PAGE=16 -e PYO3_USE_ABI3_FORWARD_COMPATIBILITY=1

python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
persist-credentials: false
- uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Build wheels"
python-version: ${{ matrix.python-version }}
- name: Build wheel
uses: PyO3/maturin-action@v1.50.1
with:
target: ${{ matrix.platform.target }}
manylinux: 2_28
docker-options: ${{ matrix.platform.maturin_docker_options }}
args: --release --locked --out dist -i python3.10 python3.11 python3.12 python3.13 python3.14
args: --release --locked --out dist -i ${{ format('python{0}', matrix.python-version) }}
env:
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
- uses: uraimo/run-on-arch-action@v2
- uses: uraimo/run-on-arch-action@v3
if: ${{ matrix.platform.arch != 'ppc64' && matrix.platform.arch != 'ppc64le'}}
name: Test wheel
with:
arch: ${{ matrix.platform.arch }}
distro: ubuntu22.04
distro: ubuntu24.04
githubToken: ${{ github.token }}
install: |
apt-get update
apt-get install -y --no-install-recommends python3 python3-pip python3-venv python3-dev cargo libffi-dev
apt-get install -y software-properties-common
add-apt-repository ppa:deadsnakes/ppa
apt-get update
apt-get install -y --no-install-recommends ${{ format('python{0}-full', matrix.python-version) }} ${{ format('python{0}-venv', matrix.python-version) }} cargo libffi-dev
# Create and use a virtual environment to avoid the externally-managed-environment error
run: |
ln -s -f /usr/bin/${{ format('python{0}', matrix.python-version) }} /usr/bin/python3
ln -s -f /usr/bin/${{ format('pip{0}', matrix.python-version) }} /usr/bin/pip3
# Workaround for QEMU bug when emulating 32-bit on 64-bit hosts
# See: https://github.com/docker/buildx/issues/395
# Use /tmp for CARGO_HOME (often tmpfs, which avoids the QEMU filesystem bug)
export CARGO_HOME=/tmp/cargo-home
python3 -m venv /tmp/venv
. /tmp/venv/bin/activate
PYTAG="cp$(python3 -c "import sys; print(f'{sys.version_info.major}{sys.version_info.minor}')")"
pip3 install dist/*-${PYTAG}-*.whl --force-reinstall
pip3 install dist/*.whl --force-reinstall
${{ env.EXECUTABLE_NAME }} --help
- name: "Upload wheels"
- name: Upload wheel
uses: actions/upload-artifact@v6
with:
name: wheels-${{ matrix.platform.target }}
name: wheels-${{ matrix.platform.target }}-py${{ matrix.python-version }}
path: dist

musllinux:
Expand All @@ -345,35 +304,34 @@ jobs:
matrix:
target:
- x86_64-unknown-linux-musl
- i686-unknown-linux-musl
python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Install OpenSSL"
python-version: ${{ matrix.python-version }}
- name: Install OpenSSL
run: sudo apt-get update && sudo apt-get install libssl-dev openssl
- name: "Build wheels"
- name: Build wheel
uses: PyO3/maturin-action@v1.50.1
with:
target: ${{ matrix.target }}
manylinux: musllinux_1_2
args: --release --locked --out dist -i python3.10 python3.11 python3.12 python3.13 python3.14
- name: "Test wheel"
args: --release --locked --out dist -i python${{ matrix.python-version }}
- name: Test wheel
if: matrix.target == 'x86_64-unknown-linux-musl'
uses: addnab/docker-run-action@v3
with:
image: alpine:latest
image: python:${{ matrix.python-version}}-alpine
options: -v ${{ github.workspace }}:/io -w /io
run: |
apk add python3 python3-dev py3-pip rust
apk add rust
python -m venv .venv
PYTAG="cp$(python -c "import sys; print(f'{sys.version_info.major}{sys.version_info.minor}')")"
.venv/bin/pip3 install dist/*-${PYTAG}-*.whl --force-reinstall
.venv/bin/pip3 install dist/*.whl --force-reinstall
.venv/bin/${{ env.EXECUTABLE_NAME }} --help
- name: "Upload wheels"
- name: Upload wheel
uses: actions/upload-artifact@v6
with:
name: wheels-${{ matrix.target }}
Expand All @@ -387,33 +345,35 @@ jobs:
platform:
- target: aarch64-unknown-linux-musl
arch: aarch64
python-version: ["3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v6
with:
submodules: recursive
- uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: "Build wheels"
python-version: ${{ matrix.python-version }}
- name: Build wheel
uses: PyO3/maturin-action@v1.50.1
with:
target: ${{ matrix.platform.target }}
manylinux: musllinux_1_2
args: --release --locked --out dist -i python3.10 python3.11 python3.12 python3.13 python3.14
args: --release --locked --out dist -i python${{ matrix.python-version }}
- uses: uraimo/run-on-arch-action@v2
name: Test wheel
with:
arch: ${{ matrix.platform.arch }}
distro: alpine_latest
githubToken: ${{ github.token }}
install: |
apk add python3 python3-dev py3-pip rust
apk add uv rust
run: |
python -m venv .venv
PYTAG="cp$(python3 -c "import sys; print(f'{sys.version_info.major}{sys.version_info.minor}')")"
.venv/bin/pip3 install dist/*-${PYTAG}-*.whl --force-reinstall
uv python install ${{ matrix.python-version }} --default
export PATH="/root/.local/bin:$PATH"
python3 -m venv .venv
.venv/bin/pip3 install dist/*.whl --force-reinstall
.venv/bin/${{ env.EXECUTABLE_NAME }} --help
- name: "Upload wheels"
- name: Upload wheel
uses: actions/upload-artifact@v6
with:
name: wheels-${{ matrix.platform.target }}
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/regenerate-skill.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
paths:
- 'crates/tower-cmd/**'
- 'crates/tower/**'
- 'skills/tower/SKILL.md'
- 'plugin/skills/tower/SKILL.md'
- '.github/workflows/regenerate-skill.yml'

concurrency:
Expand All @@ -30,18 +30,18 @@ jobs:
uses: Swatinem/rust-cache@v2

- name: Regenerate SKILL.md
run: cargo run --quiet --bin tower -- skill generate > skills/tower/SKILL.md
run: cargo run --quiet --bin tower -- skill generate > plugin/skills/tower/SKILL.md

- name: Open PR if SKILL.md changed
uses: peter-evans/create-pull-request@v7
with:
commit-message: "chore: regenerate SKILL.md"
title: "chore: regenerate SKILL.md"
body: |
`tower skill generate` output has drifted from the checked-in `skills/tower/SKILL.md`.
`tower skill generate` output has drifted from the checked-in `plugin/skills/tower/SKILL.md`.

This PR was opened automatically by the `Regenerate SKILL.md` workflow after a merge to `develop`.
branch: chore/regenerate-skill
base: develop
delete-branch: true
add-paths: skills/tower/SKILL.md
add-paths: plugin/skills/tower/SKILL.md
Loading
Loading