Files
docs/standards/mcp/templates/drone/service-docker.yaml
Vishesh 'ironeagle' Bangotra 8cc9631fe7 feat(standards): add versioning + changelog standard, release templates, and Drone version guard
Add lib page 11 (SemVer contract, pre-1.0 rule, tag==version invariant, Keep a Changelog), the releasing wiki runbook, and canonical release templates (CHANGELOG.md, VERSION). Vendor them into the MCP bundle as templates/release + release.json and expose registry://release with list/get tools. Add a changelog component to the app template. Enforce the tag<->version contract in both Drone shapes: Type A check-version and the backfill loop verify pyproject version; Type B adds a VERSION guard step.
2026-10-15 01:24:25 +05:30

278 lines
8.2 KiB
YAML

# Drone standard — Type B: Docker deployment
#
# Canonical pipelines for services that build an image, push it to the private
# registry, and run it on the Docker host (auth-server, docs, mcp).
#
# Two variants ship together:
# build-and-deploy-service — a long-running API/worker service: fetch the git
# tag, skip if the image already exists, run the
# quality gate, build, push, restart the container.
# build-and-deploy-site — a static/HTTP site: build, push, restart, on
# every push (no tag, no quality gate).
#
# Secrets (Drone): PIP_REPO_URL, PIP_USERNAME, PIP_PASSWORD (service only),
# REGISTRY_HOST, REGISTRY_USER, REGISTRY_PASS, plus the service's runtime env
# (for example MONGO_* / JWT_SECRET).
#
# Placeholders to set per repo:
# <app> image / container / network name (e.g. auth-server).
# aetos/<app> image repository on the private registry.
# <host-port> published host port; <container-port> the in-image port.
# <site> site image name for the site variant (e.g. docs, mcp).
# <env-var> runtime environment variable consumed by the service.
#
# Version guard: the tag-triggered service variant fails unless the git tag
# equals the repo-root VERSION file (see the versioning standard). The
# push-triggered site variant ships :latest only and has no version to guard.
---
kind: pipeline
type: docker
name: build-and-deploy-service
platform:
os: linux
arch: arm64
workspace:
path: /drone/src
volumes:
- name: dockersock
host:
path: /var/run/docker.sock
steps:
- name: fetch-tags
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- apk add --no-cache git
- git fetch --tags
- |
# Get latest Git tag and trim newline
LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null | tr -d '\n')
echo "Latest Git tag fetched: $LATEST_TAG"
# Save to file for downstream steps
echo "$LATEST_TAG" > /drone/src/LATEST_TAG.txt
# Read back for verification
IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
echo "Image tag read from file: $IMAGE_TAG"
# Validate
if [ -z "$IMAGE_TAG" ]; then
echo "No git tags found! Cannot continue."
exit 1
fi
- name: check-version
image: alpine:3.20
commands:
- VERSION=$(cat VERSION 2>/dev/null | tr -d '\n')
- echo "tag=$DRONE_TAG version=$VERSION"
- |
if [ -z "$VERSION" ]; then
echo "VERSION file missing or empty; add it (see versioning standard)."
exit 1
fi
- |
if [ "$DRONE_TAG" != "$VERSION" ]; then
echo "Tag '$DRONE_TAG' does not match VERSION '$VERSION'; refusing to deploy."
exit 1
fi
- name: check-remote-image
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
- echo "Checking if aetos/<app>:$IMAGE_TAG exists on remote Docker..."
- echo "Existing Docker tags for aetos/<app>:"
- docker images --format "{{.Repository}}:{{.Tag}}" | grep "^aetos/<app>" || echo "(none)"
- |
if docker image inspect aetos/<app>:$IMAGE_TAG > /dev/null 2>&1; then
echo "Docker image aetos/<app>:$IMAGE_TAG already exists; skipping build"
exit 78
else
echo "Docker image aetos/<app>:$IMAGE_TAG not found; proceeding to build..."
fi
- name: quality-gate
image: python:3.13-slim
environment:
PIP_REPO_URL:
from_secret: PIP_REPO_URL
PIP_USERNAME:
from_secret: PIP_USERNAME
PIP_PASSWORD:
from_secret: PIP_PASSWORD
commands:
- pip install --upgrade pip
- pip install --extra-index-url "https://$PIP_USERNAME:$PIP_PASSWORD@$PIP_REPO_URL/simple" -r requirements.txt
- pip install black ruff mypy
- echo "Running quality gate..."
- python -m black --check .
- python -m ruff check .
- python -m mypy .
- python -m pytest
- name: build-image
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
environment:
PIP_REPO_URL:
from_secret: PIP_REPO_URL
PIP_USERNAME:
from_secret: PIP_USERNAME
PIP_PASSWORD:
from_secret: PIP_PASSWORD
commands:
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
- echo "Building Docker image aetos/<app>:$IMAGE_TAG ..."
- |
docker build \
--network=host \
--build-arg PIP_REPO_URL="$PIP_REPO_URL" \
--build-arg PIP_USERNAME="$PIP_USERNAME" \
--build-arg PIP_PASSWORD="$PIP_PASSWORD" \
-t aetos/<app>:$IMAGE_TAG \
-t aetos/<app>:latest \
/drone/src
- name: push-image
image: docker:24
environment:
REGISTRY_HOST:
from_secret: REGISTRY_HOST
REGISTRY_USER:
from_secret: REGISTRY_USER
REGISTRY_PASS:
from_secret: REGISTRY_PASS
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
- echo "Logging into registry $REGISTRY_HOST ..."
- echo "$REGISTRY_PASS" | docker login $REGISTRY_HOST -u "$REGISTRY_USER" --password-stdin
- echo "Tagging images with registry prefix..."
- docker tag aetos/<app>:$IMAGE_TAG $REGISTRY_HOST/aetos/<app>:$IMAGE_TAG
- docker tag aetos/<app>:$IMAGE_TAG $REGISTRY_HOST/aetos/<app>:latest
- echo "Pushing aetos/<app>:$IMAGE_TAG ..."
- docker push $REGISTRY_HOST/aetos/<app>:$IMAGE_TAG
- echo "Pushing aetos/<app>:latest ..."
- docker push $REGISTRY_HOST/aetos/<app>:latest
- name: stop-old
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- echo "Stopping old container..."
- docker rm -f <app> || true
- name: run-container
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
environment:
<ENV_VAR>:
from_secret: <ENV_VAR>
commands:
- IMAGE_TAG=$(cat /drone/src/LATEST_TAG.txt | tr -d '\n')
- echo "Starting container aetos/<app>:$IMAGE_TAG ..."
- |
docker run -d \
--name <app> \
-p <host-port>:<container-port> \
--add-host private-pi:192.168.1.111 \
--restart always \
-e <ENV_VAR>=$<ENV_VAR> \
aetos/<app>:$IMAGE_TAG
trigger:
event:
- tag
---
kind: pipeline
type: docker
name: build-and-deploy-site
platform:
os: linux
arch: arm64
workspace:
path: /drone/src
volumes:
- name: dockersock
host:
path: /var/run/docker.sock
steps:
- name: build-image
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- echo "Building Docker image for aetos/<site>..."
- docker build -t aetos/<site>:latest .
- name: push-image
image: docker:24
environment:
REGISTRY_HOST:
from_secret: REGISTRY_HOST
REGISTRY_USER:
from_secret: REGISTRY_USER
REGISTRY_PASS:
from_secret: REGISTRY_PASS
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- echo "Logging into registry $REGISTRY_HOST ..."
- echo "$REGISTRY_PASS" | docker login $REGISTRY_HOST -u "$REGISTRY_USER" --password-stdin
- docker tag aetos/<site>:latest $REGISTRY_HOST/aetos/<site>:latest
- echo "Pushing image to $REGISTRY_HOST..."
- docker push $REGISTRY_HOST/aetos/<site>:latest
- name: stop-old
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- echo "Stopping old container..."
- docker rm -f <site> || true
- name: run-container
image: docker:24
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- echo "Starting aetos/<site>:latest ..."
- |
docker run -d \
--name <site> \
-p <host-port>:<container-port> \
--restart always \
aetos/<site>:latest
trigger:
event:
- push