ci: move deploy pipeline to registry images
This commit is contained in:
@@ -2,6 +2,9 @@ POSTGRES_DB=crank
|
|||||||
POSTGRES_USER=crank
|
POSTGRES_USER=crank
|
||||||
POSTGRES_PASSWORD=change-me
|
POSTGRES_PASSWORD=change-me
|
||||||
CRANK_DATABASE_URL=postgres://crank:change-me@postgres:5432/crank
|
CRANK_DATABASE_URL=postgres://crank:change-me@postgres:5432/crank
|
||||||
|
CRANK_ADMIN_API_IMAGE=crank/admin-api:dev
|
||||||
|
CRANK_MCP_SERVER_IMAGE=crank/mcp-server:dev
|
||||||
|
CRANK_UI_IMAGE=crank/ui:dev
|
||||||
CRANK_STORAGE_ROOT=/var/lib/crank/storage
|
CRANK_STORAGE_ROOT=/var/lib/crank/storage
|
||||||
CRANK_PUBLISH_HOST=127.0.0.1
|
CRANK_PUBLISH_HOST=127.0.0.1
|
||||||
CRANK_ADMIN_BIND=0.0.0.0:3001
|
CRANK_ADMIN_BIND=0.0.0.0:3001
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ jobs:
|
|||||||
run: docker build -f apps/ui/Dockerfile .
|
run: docker build -f apps/ui/Dockerfile .
|
||||||
|
|
||||||
deployment:
|
deployment:
|
||||||
name: Deployment Artifacts
|
name: Deployment Manifests
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
@@ -84,6 +84,3 @@ jobs:
|
|||||||
|
|
||||||
- name: Validate docker compose
|
- name: Validate docker compose
|
||||||
run: docker compose config -q
|
run: docker compose config -q
|
||||||
|
|
||||||
- name: Build deployable images
|
|
||||||
run: docker compose build admin-api mcp-server ui
|
|
||||||
|
|||||||
@@ -12,8 +12,77 @@ concurrency:
|
|||||||
group: deploy-${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_branch || github.ref }}
|
group: deploy-${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_branch || github.ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE_TAG: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }}
|
||||||
|
ADMIN_API_IMAGE: ghcr.io/${{ github.repository_owner }}/crank-admin-api
|
||||||
|
MCP_SERVER_IMAGE: ghcr.io/${{ github.repository_owner }}/crank-mcp-server
|
||||||
|
UI_IMAGE: ghcr.io/${{ github.repository_owner }}/crank-ui
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
build-images:
|
||||||
|
if: >
|
||||||
|
github.event_name == 'workflow_dispatch' ||
|
||||||
|
(
|
||||||
|
github.event_name == 'workflow_run' &&
|
||||||
|
github.event.workflow_run.conclusion == 'success' &&
|
||||||
|
github.event.workflow_run.head_branch == 'main'
|
||||||
|
)
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- name: admin-api
|
||||||
|
dockerfile: apps/admin-api/Dockerfile
|
||||||
|
- name: mcp-server
|
||||||
|
dockerfile: apps/mcp-server/Dockerfile
|
||||||
|
- name: ui
|
||||||
|
dockerfile: apps/ui/Dockerfile
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || github.sha }}
|
||||||
|
|
||||||
|
- name: Resolve image name
|
||||||
|
id: image
|
||||||
|
run: |
|
||||||
|
case "${{ matrix.name }}" in
|
||||||
|
admin-api) echo "name=${ADMIN_API_IMAGE}" >> "$GITHUB_OUTPUT" ;;
|
||||||
|
mcp-server) echo "name=${MCP_SERVER_IMAGE}" >> "$GITHUB_OUTPUT" ;;
|
||||||
|
ui) echo "name=${UI_IMAGE}" >> "$GITHUB_OUTPUT" ;;
|
||||||
|
*) exit 1 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to GHCR
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push ${{ matrix.name }}
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ${{ matrix.dockerfile }}
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
${{ steps.image.outputs.name }}:${{ env.IMAGE_TAG }}
|
||||||
|
${{ steps.image.outputs.name }}:main
|
||||||
|
cache-from: type=gha,scope=${{ matrix.name }}
|
||||||
|
cache-to: type=gha,mode=max,scope=${{ matrix.name }}
|
||||||
|
|
||||||
deploy:
|
deploy:
|
||||||
|
needs: build-images
|
||||||
if: >
|
if: >
|
||||||
github.event_name == 'workflow_dispatch' ||
|
github.event_name == 'workflow_dispatch' ||
|
||||||
(
|
(
|
||||||
@@ -41,28 +110,32 @@ jobs:
|
|||||||
printf '%s\n' "${{ secrets.DEPLOY_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
|
printf '%s\n' "${{ secrets.DEPLOY_KNOWN_HOSTS }}" > ~/.ssh/known_hosts
|
||||||
chmod 644 ~/.ssh/known_hosts
|
chmod 644 ~/.ssh/known_hosts
|
||||||
|
|
||||||
- name: Sync project to server
|
- name: Sync deployment files to server
|
||||||
run: |
|
run: |
|
||||||
rsync -az --delete \
|
ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" \
|
||||||
--exclude ".git" \
|
"mkdir -p '${{ secrets.DEPLOY_PATH }}'"
|
||||||
--exclude "target" \
|
rsync -az docker-compose.yml \
|
||||||
--exclude "notes" \
|
"${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/docker-compose.yml"
|
||||||
--exclude "node_modules" \
|
|
||||||
./ "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ secrets.DEPLOY_PATH }}/"
|
|
||||||
|
|
||||||
- name: Write environment file
|
- name: Write environment file
|
||||||
run: |
|
run: |
|
||||||
ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" \
|
{
|
||||||
"mkdir -p '${{ secrets.DEPLOY_PATH }}' && cat > '${{ secrets.DEPLOY_PATH }}/.env'" \
|
printf '%s\n' "${{ secrets.DEPLOY_ENV_FILE }}"
|
||||||
<<< "${{ secrets.DEPLOY_ENV_FILE }}"
|
printf 'CRANK_ADMIN_API_IMAGE=%s:%s\n' "${{ env.ADMIN_API_IMAGE }}" "${{ env.IMAGE_TAG }}"
|
||||||
|
printf 'CRANK_MCP_SERVER_IMAGE=%s:%s\n' "${{ env.MCP_SERVER_IMAGE }}" "${{ env.IMAGE_TAG }}"
|
||||||
|
printf 'CRANK_UI_IMAGE=%s:%s\n' "${{ env.UI_IMAGE }}" "${{ env.IMAGE_TAG }}"
|
||||||
|
} | ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" \
|
||||||
|
"mkdir -p '${{ secrets.DEPLOY_PATH }}' && cat > '${{ secrets.DEPLOY_PATH }}/.env'"
|
||||||
|
|
||||||
- name: Deploy with Docker Compose
|
- name: Deploy with Docker Compose
|
||||||
run: |
|
run: |
|
||||||
ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" "
|
ssh "${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}" "
|
||||||
set -e
|
set -e
|
||||||
cd '${{ secrets.DEPLOY_PATH }}'
|
cd '${{ secrets.DEPLOY_PATH }}'
|
||||||
|
echo '${{ secrets.DEPLOY_REGISTRY_TOKEN }}' | docker login ghcr.io -u '${{ secrets.DEPLOY_REGISTRY_USER }}' --password-stdin
|
||||||
docker compose config -q
|
docker compose config -q
|
||||||
docker compose up -d --build --remove-orphans
|
docker compose pull admin-api mcp-server ui
|
||||||
|
docker compose up -d --remove-orphans
|
||||||
"
|
"
|
||||||
|
|
||||||
- name: Verify health endpoints
|
- name: Verify health endpoints
|
||||||
|
|||||||
@@ -75,8 +75,8 @@ Crank - платформа для публикации внешних API в в
|
|||||||
|
|
||||||
В репозитории настроены:
|
В репозитории настроены:
|
||||||
|
|
||||||
- `CI` для Rust, UI container и deployment artifacts;
|
- `CI` для Rust, UI и deployment manifests;
|
||||||
- `CD`, который запускается после успешного `CI` на `main` или вручную;
|
- `CD`, который после успешного `CI` на `main` собирает versioned images, пушит их в `GHCR` и деплоит через `docker compose pull`;
|
||||||
- containerized deployment через `docker compose`.
|
- containerized deployment через `docker compose`.
|
||||||
|
|
||||||
## Поддерживаемые протоколы
|
## Поддерживаемые протоколы
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
|
|
||||||
## Current
|
## Current
|
||||||
|
|
||||||
### `feat/workspace-access-polish`
|
### `feat/cd-image-pipeline`
|
||||||
|
|
||||||
Status: in_progress
|
Status: in_progress
|
||||||
|
|
||||||
DoD:
|
DoD:
|
||||||
- `workspace-setup` использует live backend для role-management, member removal, export и delete
|
- CI больше не собирает deployable images без использования результата
|
||||||
- UI больше не показывает placeholder-поведение для workspace access lifecycle
|
- Deploy собирает и пушит versioned images в registry с cache
|
||||||
- backend дает workspace export и delete контракты, согласованные с UI
|
- production deploy использует `docker compose pull && docker compose up -d`, без server-side build
|
||||||
- `test-ui` остается нетронутым как fallback
|
- docs и runtime config описывают registry-based rollout
|
||||||
|
|
||||||
## Next
|
## Next
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
admin-api:
|
admin-api:
|
||||||
|
image: ${CRANK_ADMIN_API_IMAGE:-crank/admin-api:dev}
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: apps/admin-api/Dockerfile
|
dockerfile: apps/admin-api/Dockerfile
|
||||||
@@ -48,6 +49,7 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
mcp-server:
|
mcp-server:
|
||||||
|
image: ${CRANK_MCP_SERVER_IMAGE:-crank/mcp-server:dev}
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: apps/mcp-server/Dockerfile
|
dockerfile: apps/mcp-server/Dockerfile
|
||||||
@@ -74,6 +76,7 @@ services:
|
|||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
ui:
|
ui:
|
||||||
|
image: ${CRANK_UI_IMAGE:-crank/ui:dev}
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
dockerfile: apps/ui/Dockerfile
|
dockerfile: apps/ui/Dockerfile
|
||||||
|
|||||||
+22
-7
@@ -199,22 +199,37 @@ CI должен выполнять:
|
|||||||
CD для MVP должен:
|
CD для MVP должен:
|
||||||
|
|
||||||
- запускаться только после успешного `CI` на `main` или вручную;
|
- запускаться только после успешного `CI` на `main` или вручную;
|
||||||
- собирать production artifacts;
|
- собирать versioned container images;
|
||||||
|
- пушить их в container registry с cache;
|
||||||
- доставлять deployment files на целевой Linux host;
|
- доставлять deployment files на целевой Linux host;
|
||||||
- выполнять controlled restart;
|
- выполнять controlled restart через `pull`, без сборки на сервере;
|
||||||
- проверять health endpoints после запуска.
|
- проверять health endpoints после запуска.
|
||||||
|
|
||||||
Минимальный flow:
|
Минимальный flow:
|
||||||
|
|
||||||
1. push в `main`
|
1. push в `main`
|
||||||
2. успешный `CI`
|
2. успешный `CI`
|
||||||
3. build images или удаленная сборка на хосте
|
3. build and push images в registry
|
||||||
4. upload env и deployment files
|
4. upload env и deployment files
|
||||||
5. `docker compose config -q`
|
5. `docker login` на registry на целевом хосте
|
||||||
6. `docker compose up -d`
|
6. `docker compose config -q`
|
||||||
7. healthcheck verification для `ui`, `admin-api`, `mcp-server`
|
7. `docker compose pull`
|
||||||
|
8. `docker compose up -d`
|
||||||
|
9. healthcheck verification для `ui`, `admin-api`, `mcp-server`
|
||||||
|
|
||||||
Для MVP допустим deployment с удаленной сборкой на целевом Linux host через `ssh + rsync + docker compose up -d --build`, если не используется отдельный container registry.
|
Для текущего проекта базовая рекомендуемая схема:
|
||||||
|
|
||||||
|
- `GHCR` как registry;
|
||||||
|
- versioned tags по `git sha`;
|
||||||
|
- `docker/buildx` cache в GitHub Actions;
|
||||||
|
- на сервере только `pull + restart`, без `docker compose up --build`.
|
||||||
|
|
||||||
|
Для private registry на сервере нужны отдельные deployment secrets:
|
||||||
|
|
||||||
|
- `DEPLOY_REGISTRY_USER`
|
||||||
|
- `DEPLOY_REGISTRY_TOKEN`
|
||||||
|
|
||||||
|
Они используются только для `docker login` на целевом Linux host перед `docker compose pull`.
|
||||||
|
|
||||||
## 12. Что нельзя забыть
|
## 12. Что нельзя забыть
|
||||||
|
|
||||||
|
|||||||
@@ -58,6 +58,9 @@ var/crank/
|
|||||||
Минимально ожидаются:
|
Минимально ожидаются:
|
||||||
|
|
||||||
- `CRANK_DATABASE_URL`
|
- `CRANK_DATABASE_URL`
|
||||||
|
- `CRANK_ADMIN_API_IMAGE`
|
||||||
|
- `CRANK_MCP_SERVER_IMAGE`
|
||||||
|
- `CRANK_UI_IMAGE`
|
||||||
- `CRANK_STORAGE_ROOT`
|
- `CRANK_STORAGE_ROOT`
|
||||||
- `CRANK_ADMIN_BIND`
|
- `CRANK_ADMIN_BIND`
|
||||||
- `CRANK_MCP_BIND`
|
- `CRANK_MCP_BIND`
|
||||||
@@ -116,6 +119,7 @@ Demo/deployment:
|
|||||||
- включенная app-level auth-защита admin-api;
|
- включенная app-level auth-защита admin-api;
|
||||||
- стабильный `Streamable HTTP` endpoint для MCP.
|
- стабильный `Streamable HTTP` endpoint для MCP.
|
||||||
- containerized runtime через `Docker` и `docker-compose`.
|
- containerized runtime через `Docker` и `docker-compose`.
|
||||||
|
- registry-backed image rollout через `GHCR` или совместимый registry.
|
||||||
|
|
||||||
### Auth env
|
### Auth env
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user