Skip to content

Commit b57b966

Browse files
committed
feat: refactor Docker build workflow for better maintainability
- Extract Docker operations to reusable scripts/docker.sh - Move Docker push from push.yml to create-release action - Add manual Docker targets to make.deploy (docker-build, docker-push) - Update env.example with Docker configuration variables - Optimize CI/CD by skipping Docker for dev releases - Add PR validation for Docker builds without pushing - Document required GitHub secrets for ECR operations
1 parent 3d765dc commit b57b966

File tree

8 files changed

+487
-45
lines changed

8 files changed

+487
-45
lines changed

.github/actions/create-release/action.yml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: 'Create Release'
2-
description: 'Build Python package, MCPB package, and create GitHub release'
2+
description: 'Build Python package, MCPB package, Docker image, and create GitHub release'
33
inputs:
44
package-version:
55
description: 'Version from git tag (e.g., 0.5.9-dev-20250904075318)'
@@ -12,6 +12,10 @@ inputs:
1212
description: 'Skip existing packages during upload'
1313
required: false
1414
default: 'false'
15+
build-docker:
16+
description: 'Build and push Docker image (true/false)'
17+
required: false
18+
default: 'true'
1519

1620
outputs:
1721
release-url:
@@ -49,6 +53,19 @@ runs:
4953
shell: bash
5054
run: make release-zip
5155

56+
- name: Build and push Docker image
57+
if: ${{ inputs.build-docker == 'true' }}
58+
shell: bash
59+
env:
60+
VERSION: ${{ inputs.package-version }}
61+
ECR_REGISTRY: ${{ env.ECR_REGISTRY }}
62+
AWS_ACCOUNT_ID: ${{ env.AWS_ACCOUNT_ID }}
63+
AWS_DEFAULT_REGION: ${{ env.AWS_DEFAULT_REGION }}
64+
run: |
65+
echo "🐳 Building and pushing Docker image with scripts/docker.sh"
66+
chmod +x scripts/docker.sh
67+
./scripts/docker.sh push --version "$VERSION"
68+
5269
- name: Create GitHub Release
5370
id: create-release
5471
uses: softprops/action-gh-release@v2

.github/workflows/pr.yml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ on:
66
branches: ['**']
77
push:
88
tags: ['v*-dev-*']
9+
910
jobs:
1011
test:
1112
runs-on: ubuntu-latest
@@ -41,6 +42,13 @@ jobs:
4142
QUILT_TEST_PACKAGE: ${{ secrets.QUILT_TEST_PACKAGE }}
4243
QUILT_TEST_ENTRY: ${{ secrets.QUILT_TEST_ENTRY }}
4344

45+
- name: Test Docker build (no push)
46+
if: github.event_name == 'pull_request'
47+
run: |
48+
echo "🐳 Testing Docker build for PR (no push)..."
49+
chmod +x scripts/docker.sh
50+
./scripts/docker.sh build
51+
4452
- name: Debug GitHub context for dev-release
4553
run: |
4654
echo "=== GitHub Context Debug ==="
@@ -75,6 +83,7 @@ jobs:
7583
with:
7684
python-version: '3.11'
7785
include-nodejs: 'true'
86+
7887
- name: Debug dev-release context
7988
run: |
8089
echo "=== Dev-Release Context Debug ==="
@@ -94,9 +103,11 @@ jobs:
94103
TAG_VERSION=${GITHUB_REF#refs/tags/v}
95104
echo "tag_version=$TAG_VERSION" >> $GITHUB_OUTPUT
96105
echo "Dev Tag Version: $TAG_VERSION"
106+
97107
- name: Create dev release
98108
uses: ./.github/actions/create-release
99109
with:
100110
package-version: ${{ steps.version.outputs.tag_version }}
101111
pypi-repository-url: https://test.pypi.org/legacy/
102-
skip-existing: true
112+
skip-existing: true
113+
build-docker: 'false' # Skip Docker for development releases

.github/workflows/push.yml

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,6 @@ jobs:
7777
echo "tag_version=$TAG_VERSION" >> $GITHUB_OUTPUT
7878
echo "Production Tag Version: $TAG_VERSION"
7979
80-
- name: Create production release
81-
uses: ./.github/actions/create-release
82-
with:
83-
package-version: ${{ steps.version.outputs.tag_version }}
84-
# pypi-repository-url defaults to '' for PyPI
85-
8680
- name: Configure AWS credentials for ECR
8781
uses: aws-actions/configure-aws-credentials@v4
8882
with:
@@ -93,36 +87,15 @@ jobs:
9387
- name: Login to Amazon ECR
9488
uses: aws-actions/amazon-ecr-login@v2
9589

96-
- name: Build and publish Docker image
90+
- name: Create production release
91+
uses: ./.github/actions/create-release
92+
with:
93+
package-version: ${{ steps.version.outputs.tag_version }}
94+
build-docker: 'true'
95+
# pypi-repository-url defaults to '' for PyPI
9796
env:
98-
VERSION: ${{ steps.version.outputs.tag_version }}
99-
ECR_REGISTRY: ${{ secrets.ECR_REGISTRY }}
100-
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
101-
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION || 'us-east-1' }}
102-
shell: bash
103-
run: |
104-
set -euo pipefail
105-
106-
registry="$ECR_REGISTRY"
107-
if [ -z "$registry" ]; then
108-
if [ -z "$AWS_ACCOUNT_ID" ]; then
109-
echo "ECR registry not configured" >&2
110-
exit 1
111-
fi
112-
registry="$AWS_ACCOUNT_ID.dkr.ecr.${AWS_DEFAULT_REGION:-us-east-1}.amazonaws.com"
113-
fi
114-
115-
image_tags=$(uv run python scripts/docker_image.py --registry "$registry" --version "$VERSION")
116-
117-
first_tag=$(echo "$image_tags" | head -n 1)
118-
docker build --file Dockerfile --tag "$first_tag" .
119-
120-
echo "$image_tags" | tail -n +2 | while read -r tag; do
121-
[ -z "$tag" ] && continue
122-
docker tag "$first_tag" "$tag"
123-
done
124-
125-
echo "$image_tags" | while read -r tag; do
126-
[ -z "$tag" ] && continue
127-
docker push "$tag"
128-
done
97+
# Docker registry configuration - these secrets are optional
98+
# If ECR_REGISTRY is not set, falls back to constructing from AWS_ACCOUNT_ID
99+
ECR_REGISTRY: ${{ secrets.ECR_REGISTRY || '' }}
100+
AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID || '' }}
101+
AWS_DEFAULT_REGION: ${{ secrets.AWS_DEFAULT_REGION || 'us-east-1' }}

AGENTS.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,12 @@ For this repository's specific commands and permissions, see this CLAUDE.md file
312312
- `make release` - Create and push release tag
313313
- `make release-dev` - Create and push development tag
314314

315+
**Docker Operations (make.deploy):**
316+
317+
- `make docker-build` - Build Docker image locally
318+
- `make docker-push` - Build and push Docker image to ECR (requires VERSION)
319+
- `make docker-push-dev` - Build and push development Docker image
320+
315321
**Coordination & Utilities:**
316322

317323
- `make help` - Show all available targets organized by category
@@ -421,9 +427,53 @@ The following permissions are granted for this repository:
421427
- When running the integration test locally, Docker must be available and the build takes ~45s on warm caches. Expect the test to leave behind pulled base images but no running containers.
422428
- Claude Desktop still requires stdio transports; use a FastMCP proxy (`FastMCP.as_proxy(...).run(transport='stdio')`) and pass `--project /path/to/quilt-mcp-server` to `uv run` so `fastmcp` resolves correctly.
423429

430+
### Docker Build and Deployment Refactoring (2025-09-22)
431+
432+
**Script-based Docker Operations:**
433+
434+
- All Docker operations extracted to `scripts/docker.sh` for reusability and local testing
435+
- Script supports both `build` (local testing) and `push` (ECR deployment) commands
436+
- Integrates with existing `scripts/docker_image.py` for consistent tag generation
437+
- Supports dry-run mode via `--dry-run` for testing workflow changes
438+
439+
**GitHub Actions Integration:**
440+
441+
- Production releases (tags matching `v*` but not `v*-dev-*`) build and push Docker images automatically
442+
- Development releases skip Docker builds to reduce CI/CD time and resource usage
443+
- PR builds test Docker image building without pushing (build-only validation)
444+
- Docker operations moved from `push.yml` workflow into `create-release` action for better encapsulation
445+
446+
**Makefile Integration:**
447+
448+
- `make docker-build` - Build locally for development and testing
449+
- `make docker-push` - Build and push to ECR (requires VERSION environment variable)
450+
- `make docker-push-dev` - Build and push with timestamp-based development tags
451+
- All Docker targets include proper dependency checking for Docker daemon and required tools
452+
453+
**GitHub Secrets Configuration:**
454+
455+
Required secrets for Docker operations:
456+
- `ECR_REGISTRY` - ECR registry URL (preferred)
457+
- `AWS_ACCOUNT_ID` - AWS account ID (fallback for registry construction)
458+
- `AWS_DEFAULT_REGION` - AWS region (defaults to us-east-1)
459+
- Existing AWS credentials (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) for ECR login
460+
461+
**Environment Variable Support:**
462+
463+
- `scripts/docker.sh` respects all environment variables from `env.example`
464+
- `ECR_REGISTRY`, `AWS_ACCOUNT_ID`, `AWS_DEFAULT_REGION` for registry configuration
465+
- `VERSION` for overriding image version tags
466+
- `DOCKER_IMAGE_NAME` for custom image naming (defaults to `quilt-mcp-server`)
467+
424468
## important-instruction-reminders
425469

426470
Do what has been asked; nothing more, nothing less.
427471
NEVER create files unless they're absolutely necessary for achieving your goal.
428472
ALWAYS prefer editing an existing file to creating a new one.
429473
NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.
474+
475+
# important-instruction-reminders
476+
Do what has been asked; nothing more, nothing less.
477+
NEVER create files unless they're absolutely necessary for achieving your goal.
478+
ALWAYS prefer editing an existing file to creating a new one.
479+
NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.

Makefile

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Quilt MCP Server - Consolidated Build System
2-
#
2+
#
33
# This Makefile consolidates all build workflows into organized includes.
44
# Development targets are in make.dev, production targets are in make.deploy.
55

@@ -38,6 +38,11 @@ help:
3838
@echo " make release - Create and push release tag"
3939
@echo " make release-dev - Create and push development tag"
4040
@echo ""
41+
@echo "🐳 Docker Operations (make.deploy):"
42+
@echo " make docker-build - Build Docker image locally"
43+
@echo " make docker-push - Build and push Docker image to ECR (requires VERSION)"
44+
@echo " make docker-push-dev - Build and push development Docker image"
45+
@echo ""
4146
@echo "🔢 Version Management:"
4247
@echo " make bump-patch - Bump patch version (1.2.3 → 1.2.4)"
4348
@echo " make bump-minor - Bump minor version (1.2.3 → 1.3.0)"
@@ -130,4 +135,4 @@ release-major: bump-major
130135
git commit -m "bump: major version to $$VERSION"; \
131136
echo "✅ Committed version bump to $$VERSION"
132137
@echo "🏷️ Creating release tag..."
133-
@scripts/release.sh release
138+
@scripts/release.sh release

env.example

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ FASTMCP_TRANSPORT=stdio
2323
# ngrok Configuration for remote tunneling (optional)
2424
NGROK_DOMAIN=my-free-domain.ngrok-free.app
2525

26+
# Docker Configuration
27+
# ECR registry URL (required for Docker push operations)
28+
# Format: {account_id}.dkr.ecr.{region}.amazonaws.com
29+
ECR_REGISTRY=123456789.dkr.ecr.us-east-1.amazonaws.com
30+
31+
# Docker image configuration (optional)
32+
DOCKER_IMAGE_NAME=quilt-mcp-server
33+
2634
# Python packaging (uv)
2735
# Required for publishing: set either UV_PUBLISH_TOKEN or both UV_PUBLISH_USERNAME/UV_PUBLISH_PASSWORD.
2836
# Leave unset to build locally without publishing.
@@ -34,4 +42,4 @@ UV_PUBLISH_PASSWORD=testpypi-password
3442
PYPI_PUBLISH_URL=https://test.pypi.org/legacy/
3543

3644
# Optional: Override dist output directory (defaults to dist/)
37-
DIST_DIR=dist
45+
DIST_DIR=dist

make.deploy

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ RELEASE_ZIP := $(PACKAGE_ID)-release.zip
2222
ASSET_FILES := $(wildcard $(ASSETS_DIR)/*)
2323
APP_FILES := $(shell find $(APP_DIR)/quilt_mcp -name "*.py" 2>/dev/null || true)
2424

25-
.PHONY: deploy-build mcpb mcpb-validate python-dist python-publish release-zip deploy-clean check-tools
25+
.PHONY: deploy-build mcpb mcpb-validate python-dist python-publish release-zip deploy-clean check-tools docker-build docker-push docker-push-dev check-docker-tools
2626

2727
# Check for required tools
2828
check-tools:
@@ -31,6 +31,12 @@ check-tools:
3131
@command -v mcpb >/dev/null 2>&1 || { echo "❌ mcpb not found - run: npm install -g @anthropic-ai/mcpb"; exit 1; }
3232
@echo "✅ All required tools found"
3333

34+
# Check for Docker-specific tools
35+
check-docker-tools:
36+
@command -v docker >/dev/null 2>&1 || { echo "❌ Docker not found - install Docker"; exit 1; }
37+
@docker info >/dev/null 2>&1 || { echo "❌ Docker daemon not running or not accessible"; exit 1; }
38+
@echo "✅ Docker tools available"
39+
3440
# Build Environment Preparation
3541
deploy-build: check-tools
3642
@echo "Preparing production build environment..."
@@ -120,6 +126,28 @@ $(RELEASE_ZIP): mcpb-validate $(ASSETS_DIR)/README.md $(ASSETS_DIR)/check-mcpb.s
120126
release-zip: $(RELEASE_ZIP)
121127
@echo "✅ Release bundle $(RELEASE_ZIP) ready for distribution"
122128

129+
# Docker Operations
130+
docker-build: check-docker-tools
131+
@echo "🐳 Building Docker image locally..."
132+
@./scripts/docker.sh build
133+
@echo "✅ Docker build completed"
134+
135+
docker-push: check-docker-tools
136+
@echo "🐳 Building and pushing Docker image..."
137+
@if [ -z "$(VERSION)" ]; then \
138+
echo "❌ VERSION is required for docker-push. Use: make docker-push VERSION=1.2.3"; \
139+
exit 1; \
140+
fi
141+
@VERSION=$(PACKAGE_VERSION) ./scripts/docker.sh push --version $(PACKAGE_VERSION)
142+
@echo "✅ Docker push completed"
143+
144+
docker-push-dev: check-docker-tools
145+
@echo "🐳 Building and pushing development Docker image..."
146+
@DEV_VERSION="$(PACKAGE_VERSION)-dev-$(shell date +%Y%m%d%H%M%S)"
147+
@echo "Using development version: $$DEV_VERSION"
148+
@VERSION=$$DEV_VERSION ./scripts/docker.sh push --version $$DEV_VERSION --no-latest
149+
@echo "✅ Development Docker push completed"
150+
123151
# Release Tagging Targets
124152
release-tag:
125153
@echo "Creating release tag..."
@@ -144,4 +172,4 @@ deploy-clean:
144172
@echo "Cleaning build artifacts..."
145173
@rm -rf $(BUILD_DIR) $(DIST_DIR)
146174
@rm -f *.mcpb # Clean MCPB packages from root
147-
@echo "✅ Deploy cleanup completed"
175+
@echo "✅ Deploy cleanup completed"

0 commit comments

Comments
 (0)