Skip to content

Commit 2e3a37c

Browse files
committed
Add Docker support, Python 3.12 compatibility, and streamline CI/CD
Docker Implementation: - Add multi-stage Dockerfile with pro/pre/dev variants - Create docker-publish.yml workflow for automated ghcr.io publishing - Add compose.yaml with service inheritance for easy local development - Include .env.example for Docker configuration Python & Build Improvements: - Add Python 3.12 support and test in CI matrix - 3.12 doesn't support the async Python REPL in the MCP browser - Migrate from Poetry to Hatchling build backend - Update workflows to use dynamic versioning without commit noise - Remove version bump commits from publish workflow Settings & Configuration: - Standardize all settings classes to use BaseSettings - Add validate_assignment=True across all config classes - Clean up environment variable handling - Fix MAGG_READ_ONLY to be build arg in Docker Documentation Updates: - Recommend `uv tool install magg` as primary installation - Add Docker usage section with examples - Update workflows documentation Testing & CI: - Run tests in Docker containers for all Python versions - Add matrix testing for Python 3.12, 3.13, and .python-version - Remove redundant branch-protection.yml workflow - Fix test environment variable handling Other Changes: - Add initial MAGG_DEBUG env var support - Fix mbro connection string formatting - Improve server startup logging and error handling Signed-off-by: Phillip Sitbon <phillip.sitbon@gmail.com>
1 parent 61d1b15 commit 2e3a37c

File tree

20 files changed

+873
-277
lines changed

20 files changed

+873
-277
lines changed

.env.example

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Docker Registry Configuration
2+
# REGISTRY=ghcr.io/<your-github-username>
3+
4+
# Image source prefix (defaults to $USER)
5+
# SOURCE=local
6+
7+
# Config volume path (defaults to named volume 'magg-config' except for dev)
8+
# MAGG_CONFIG_VOLUME=./.magg
9+
10+
# Authentication
11+
# MAGG_PRIVATE_KEY= # generate with `magg auth private-key --oneline`
12+
# MAGG_JWT= # For clients - generate with `magg auth jwt -q`
13+
14+
# Logging
15+
# MAGG_LOG_LEVEL=WARNING
16+
# MAGG_DEBUG=0
17+
# MAGG_QUIET=0
18+
19+
# Other options
20+
# MAGG_CONFIG_PATH=./magg/config.json
21+
# MAGG_SELF_PREFIX=magg # The prefix used in the MCP server for magg's own tools

.github/workflows/README.md

Lines changed: 0 additions & 89 deletions
This file was deleted.

.github/workflows/branch-protection.yml

Lines changed: 0 additions & 35 deletions
This file was deleted.
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
name: Build and Publish Docker Images
2+
3+
on:
4+
push:
5+
branches: [ beta ]
6+
tags: [ 'magg/v*' ]
7+
pull_request:
8+
branches: [ main, beta ]
9+
workflow_dispatch:
10+
11+
env:
12+
REGISTRY: ghcr.io
13+
IMAGE_NAME: ${{ github.repository }}
14+
15+
jobs:
16+
get-python-version:
17+
runs-on: ubuntu-latest
18+
outputs:
19+
version: ${{ steps.python-version.outputs.version }}
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
24+
- name: Read Python version
25+
id: python-version
26+
run: |
27+
echo "version=$(cat .python-version)" >> $GITHUB_OUTPUT
28+
29+
test-dev:
30+
needs: get-python-version
31+
runs-on: ubuntu-latest
32+
permissions:
33+
contents: read
34+
packages: write
35+
36+
strategy:
37+
matrix:
38+
python-version: ['3.12', '3.13', '${{ needs.get-python-version.outputs.version }}']
39+
fail-fast: false
40+
41+
steps:
42+
- name: Checkout
43+
uses: actions/checkout@v4
44+
45+
- name: Set up Docker Buildx
46+
uses: docker/setup-buildx-action@v3
47+
48+
- name: Log in to Container Registry
49+
uses: docker/login-action@v3
50+
with:
51+
registry: ${{ env.REGISTRY }}
52+
username: ${{ github.actor }}
53+
password: ${{ secrets.GITHUB_TOKEN }}
54+
55+
- name: Extract metadata
56+
id: meta
57+
uses: docker/metadata-action@v5
58+
with:
59+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
60+
tags: |
61+
type=ref,event=branch,suffix=-dev-py${{ matrix.python-version }}
62+
type=ref,event=pr,suffix=-dev-py${{ matrix.python-version }}
63+
type=semver,pattern={{version}},suffix=-dev-py${{ matrix.python-version }}
64+
type=semver,pattern={{major}}.{{minor}},suffix=-dev-py${{ matrix.python-version }}
65+
type=raw,value=latest,enable={{is_default_branch}},suffix=-dev-py${{ matrix.python-version }}
66+
67+
- name: Build dev image
68+
uses: docker/build-push-action@v6
69+
with:
70+
context: .
71+
file: ./dockerfile
72+
target: dev
73+
push: false
74+
tags: ${{ steps.meta.outputs.tags }}
75+
labels: ${{ steps.meta.outputs.labels }}
76+
build-args: |
77+
PYTHON_VERSION=${{ matrix.python-version }}
78+
cache-from: type=gha
79+
cache-to: type=gha,mode=max
80+
load: true
81+
82+
- name: Run tests in container
83+
run: |
84+
docker run --rm \
85+
-e MAGG_LOG_LEVEL= \
86+
${{ steps.meta.outputs.tags }} \
87+
pytest -v
88+
89+
- name: Push dev image
90+
if: github.event_name != 'pull_request'
91+
uses: docker/build-push-action@v6
92+
with:
93+
context: .
94+
file: ./dockerfile
95+
target: dev
96+
push: true
97+
tags: ${{ steps.meta.outputs.tags }}
98+
labels: ${{ steps.meta.outputs.labels }}
99+
build-args: |
100+
PYTHON_VERSION=${{ matrix.python-version }}
101+
cache-from: type=gha
102+
103+
build-and-push:
104+
needs: test-dev
105+
runs-on: ubuntu-latest
106+
permissions:
107+
contents: read
108+
packages: write
109+
110+
strategy:
111+
matrix:
112+
include:
113+
- target: pro
114+
suffix: ""
115+
- target: pre
116+
suffix: "-pre"
117+
118+
steps:
119+
- name: Checkout
120+
uses: actions/checkout@v4
121+
122+
- name: Set up Docker Buildx
123+
uses: docker/setup-buildx-action@v3
124+
125+
- name: Log in to Container Registry
126+
uses: docker/login-action@v3
127+
with:
128+
registry: ${{ env.REGISTRY }}
129+
username: ${{ github.actor }}
130+
password: ${{ secrets.GITHUB_TOKEN }}
131+
132+
- name: Extract metadata
133+
id: meta
134+
uses: docker/metadata-action@v5
135+
with:
136+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
137+
tags: |
138+
type=ref,event=branch,suffix=${{ matrix.suffix }}
139+
type=ref,event=pr,suffix=${{ matrix.suffix }}
140+
type=semver,pattern={{version}},suffix=${{ matrix.suffix }}
141+
type=semver,pattern={{major}}.{{minor}},suffix=${{ matrix.suffix }}
142+
type=raw,value=latest,enable={{is_default_branch}},suffix=${{ matrix.suffix }}
143+
144+
- name: Build and push Docker image
145+
uses: docker/build-push-action@v6
146+
with:
147+
context: .
148+
file: ./dockerfile
149+
target: ${{ matrix.target }}
150+
push: ${{ github.event_name != 'pull_request' }}
151+
tags: ${{ steps.meta.outputs.tags }}
152+
labels: ${{ steps.meta.outputs.labels }}
153+
cache-from: type=gha
154+
cache-to: type=gha,mode=max

.github/workflows/publish.yml

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -70,41 +70,52 @@ jobs:
7070
git config commit.gpgsign true
7171
git config tag.gpgsign true
7272
73-
- name: Create publish commit and build
73+
- name: Build and tag release
7474
run: |
75-
# This command does everything:
76-
# - Updates version in pyproject.toml
77-
# - Builds the package
78-
# - Creates signed commit with version/hash info
79-
# - Creates signed tag
80-
uv run poe create-publish-commit
75+
# Get commit count
76+
C=$(git rev-list --count HEAD)
8177
82-
# Export values for the release step
83-
echo "VERSION=$(uv version --short)" >> $GITHUB_ENV
78+
# Get current version
79+
V=$(uv version --short)
8480
85-
# Get changelog since last release with rich formatting
86-
echo "CHANGELOG<<EOF" >> $GITHUB_ENV
81+
# Create version string and export
82+
VERSION="${V}.${C}"
83+
echo "VERSION=${VERSION}" >> $GITHUB_ENV
84+
85+
# Update version in pyproject.toml
86+
UV_FROZEN=true uv version "${VERSION}"
8787
88-
git log magg/latest..${{ github.sha }} --pretty=format:'### [%s](%h)%n**Author: %an <%ae>**%n*Date: %ad*%n%n%b%n' | sed -e '/^### /!{/^$/!s/^/> /}' -e 's/^$/>/g' >> $GITHUB_ENV
88+
# Build the package
89+
UV_FROZEN=true uv build
8990
91+
# Create a new version tag and export name
92+
TAG="magg/v${VERSION}"
93+
echo "TAG=${TAG}" >> $GITHUB_ENV
94+
TAG_MESSAGE="[Automatic] Release Version v${VERSION} from $(git rev-parse --short HEAD)"
95+
git tag -s -m "${TAG_MESSAGE}" "${TAG}"
96+
97+
# Update latest tag after getting changelog
98+
LATEST_TAG="magg/latest"
99+
100+
# Get changelog since last release with rich formatting
101+
echo "CHANGELOG<<EOF" >> $GITHUB_ENV
102+
git log ${LATEST_TAG}..${{ github.sha }} --pretty=format:'### [%s](%h)%n**Author: %an <%ae>**%n*Date: %ad*%n%n%b%n' | sed -e '/^### /!{/^$/!s/^/> /}' -e 's/^$/>/g' >> $GITHUB_ENV
90103
echo "EOF" >> $GITHUB_ENV
91104
92105
# Delete remote latest tag FIRST (before creating new one)
93-
git push origin :refs/tags/magg/latest || true
106+
git push origin :refs/tags/${LATEST_TAG} || true
94107
95108
# Now create the new latest tag locally
96-
git tag -d magg/latest || true
97-
git tag --no-sign magg/latest
98-
99-
- name: Push changes back to main
100-
run: |
101-
git push origin main
109+
git tag -d ${LATEST_TAG} || true
110+
git tag --no-sign ${LATEST_TAG}
111+
112+
# Push the new latest tag to remote
102113
git push origin --tags
103-
114+
104115
- name: Create GitHub Release
105116
uses: softprops/action-gh-release@v2
106117
with:
107-
tag_name: magg/v${{ env.VERSION }}
118+
tag_name: ${{ env.TAG }}
108119
name: 🧲 Magg Release v${{ env.VERSION }}
109120
body: |
110121
Automatically generated release triggered by commit ${{ github.sha }}
@@ -127,4 +138,4 @@ jobs:
127138
env:
128139
PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
129140
run: |
130-
uv run poe pypi-publish
141+
uv publish --token $PYPI_TOKEN

0 commit comments

Comments
 (0)