mirror of
https://github.com/dartdavros/chatballs.git
synced 2026-10-05 17:14:59 +03:00
Go-шаблон был обёрнут во второй слой фигурных скобок и выводил строку
«{{json .Manifest.Digest}}» вместо digest — сборка gateway падала на
невалидном FROM.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
197 lines
8.2 KiB
YAML
197 lines
8.2 KiB
YAML
# Публикация Chatballs: образы в GitHub Container Registry + релизный compose.yaml.
|
||
#
|
||
# Смысл: человек не должен ни собирать продукт, ни распаковывать бандл, чтобы
|
||
# его запустить. По тегу vX.Y.Z workflow собирает четыре образа (backend,
|
||
# frontend, gateway с Caddyfile внутри, postgres с init-скриптами внутри),
|
||
# публикует их в ghcr.io и прикладывает к релизу один файл — compose.yaml с
|
||
# ссылками по digest. Установка на чистый хост:
|
||
#
|
||
# curl -fsSL <ссылка на compose.yaml> -o compose.yaml
|
||
# docker compose up -d --wait
|
||
#
|
||
# release.env кладётся рядом для тех, кто разворачивает через `chatballs`
|
||
# (закрытый контур): там нужны те же digest'ы отдельным файлом.
|
||
#
|
||
# Ничего настраивать не нужно: путь образов выводится из github.repository,
|
||
# публикация идёт встроенным GITHUB_TOKEN.
|
||
|
||
name: release
|
||
|
||
on:
|
||
push:
|
||
tags:
|
||
- "v*"
|
||
workflow_dispatch:
|
||
inputs:
|
||
version:
|
||
description: "Версия релиза (например 1.0.0)"
|
||
required: true
|
||
|
||
permissions:
|
||
contents: write
|
||
packages: write
|
||
|
||
env:
|
||
APP_DIR: .
|
||
REGISTRY: ghcr.io
|
||
# Сторонние образы пинуются тем же способом, что и свои: по digest.
|
||
POSTGRES_BASE_IMAGE: pgvector/pgvector:pg16
|
||
GATEWAY_BASE_IMAGE: caddy:2.8.4
|
||
REDIS_IMAGE: redis:7-alpine
|
||
COTURN_IMAGE: coturn/coturn:4.6
|
||
|
||
jobs:
|
||
images:
|
||
runs-on: ubuntu-latest
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Версия релиза
|
||
id: version
|
||
run: |
|
||
version="${{ inputs.version }}"
|
||
[ -n "$version" ] || version="${GITHUB_REF_NAME#v}"
|
||
case "$version" in
|
||
*[!A-Za-z0-9._-]*|"") echo "Недопустимая версия: $version" >&2; exit 1 ;;
|
||
esac
|
||
echo "value=$version" >> "$GITHUB_OUTPUT"
|
||
echo "repo=$(echo '${{ github.repository }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
|
||
|
||
- uses: docker/setup-buildx-action@v3
|
||
|
||
- uses: docker/login-action@v3
|
||
with:
|
||
registry: ${{ env.REGISTRY }}
|
||
username: ${{ github.actor }}
|
||
password: ${{ secrets.GITHUB_TOKEN }}
|
||
|
||
# Базовые образы закрепляем до сборки: тогда digest наших образов
|
||
# однозначно отвечает известному входу, а не плавающему тегу.
|
||
- name: Digest базовых образов
|
||
id: bases
|
||
run: |
|
||
set -eu
|
||
digest_of() {
|
||
docker buildx imagetools inspect "$1" --format '{{json .Manifest.Digest}}' | tr -d '"'
|
||
}
|
||
echo "postgres=${POSTGRES_BASE_IMAGE}@$(digest_of "$POSTGRES_BASE_IMAGE")" >> "$GITHUB_OUTPUT"
|
||
echo "gateway=${GATEWAY_BASE_IMAGE}@$(digest_of "$GATEWAY_BASE_IMAGE")" >> "$GITHUB_OUTPUT"
|
||
echo "redis=${REDIS_IMAGE}@$(digest_of "$REDIS_IMAGE")" >> "$GITHUB_OUTPUT"
|
||
echo "coturn=${COTURN_IMAGE}@$(digest_of "$COTURN_IMAGE")" >> "$GITHUB_OUTPUT"
|
||
|
||
- name: Backend
|
||
id: backend
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: ${{ env.APP_DIR }}
|
||
file: ${{ env.APP_DIR }}/apps/backend/Dockerfile.production
|
||
push: true
|
||
tags: |
|
||
${{ env.REGISTRY }}/${{ steps.version.outputs.repo }}/backend:${{ steps.version.outputs.value }}
|
||
${{ env.REGISTRY }}/${{ steps.version.outputs.repo }}/backend:latest
|
||
cache-from: type=gha
|
||
cache-to: type=gha,mode=max
|
||
|
||
- name: Frontend
|
||
id: frontend
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: ${{ env.APP_DIR }}
|
||
file: ${{ env.APP_DIR }}/deploy/docker/frontend.Dockerfile
|
||
push: true
|
||
tags: |
|
||
${{ env.REGISTRY }}/${{ steps.version.outputs.repo }}/frontend:${{ steps.version.outputs.value }}
|
||
${{ env.REGISTRY }}/${{ steps.version.outputs.repo }}/frontend:latest
|
||
cache-from: type=gha
|
||
cache-to: type=gha,mode=max
|
||
|
||
# Шлюз и база — свои образы: Caddyfile и init-скрипты живут внутри них,
|
||
# а не монтируются с хоста. Ради этого установка и стала одним файлом.
|
||
- name: Gateway
|
||
id: gateway
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: ${{ env.APP_DIR }}
|
||
file: ${{ env.APP_DIR }}/deploy/docker/gateway.Dockerfile
|
||
build-args: |
|
||
CHATBALLS_GATEWAY_BASE_IMAGE=${{ steps.bases.outputs.gateway }}
|
||
push: true
|
||
tags: |
|
||
${{ env.REGISTRY }}/${{ steps.version.outputs.repo }}/gateway:${{ steps.version.outputs.value }}
|
||
${{ env.REGISTRY }}/${{ steps.version.outputs.repo }}/gateway:latest
|
||
cache-from: type=gha
|
||
cache-to: type=gha,mode=max
|
||
|
||
- name: Postgres
|
||
id: postgres
|
||
uses: docker/build-push-action@v6
|
||
with:
|
||
context: ${{ env.APP_DIR }}
|
||
file: ${{ env.APP_DIR }}/deploy/docker/postgres.Dockerfile
|
||
build-args: |
|
||
CHATBALLS_POSTGRES_BASE_IMAGE=${{ steps.bases.outputs.postgres }}
|
||
push: true
|
||
tags: |
|
||
${{ env.REGISTRY }}/${{ steps.version.outputs.repo }}/postgres:${{ steps.version.outputs.value }}
|
||
${{ env.REGISTRY }}/${{ steps.version.outputs.repo }}/postgres:latest
|
||
cache-from: type=gha
|
||
cache-to: type=gha,mode=max
|
||
|
||
- name: Релизный compose.yaml и release.env
|
||
id: artifacts
|
||
run: |
|
||
set -eu
|
||
version="${{ steps.version.outputs.value }}"
|
||
prefix="${{ env.REGISTRY }}/${{ steps.version.outputs.repo }}"
|
||
|
||
backend="$prefix/backend:$version@${{ steps.backend.outputs.digest }}"
|
||
frontend="$prefix/frontend:$version@${{ steps.frontend.outputs.digest }}"
|
||
gateway="$prefix/gateway:$version@${{ steps.gateway.outputs.digest }}"
|
||
postgres="$prefix/postgres:$version@${{ steps.postgres.outputs.digest }}"
|
||
redis="${{ steps.bases.outputs.redis }}"
|
||
coturn="${{ steps.bases.outputs.coturn }}"
|
||
|
||
python3 scripts/pin-release-compose.py \
|
||
--source compose.yaml \
|
||
--output dist/compose.yaml \
|
||
--version "$version" \
|
||
--pin "CHATBALLS_BACKEND_IMAGE=$backend" \
|
||
--pin "CHATBALLS_FRONTEND_IMAGE=$frontend" \
|
||
--pin "CHATBALLS_GATEWAY_IMAGE=$gateway" \
|
||
--pin "CHATBALLS_POSTGRES_IMAGE=$postgres" \
|
||
--pin "CHATBALLS_REDIS_IMAGE=$redis" \
|
||
--pin "CHATBALLS_COTURN_IMAGE=$coturn"
|
||
|
||
{
|
||
echo "# release.env — digest-пины релиза $version для \`chatballs deploy\`."
|
||
echo "# Тем, кто ставит одной командой, он не нужен: всё уже внутри compose.yaml."
|
||
echo "CHATBALLS_VERSION=$version"
|
||
echo "CHATBALLS_BACKEND_IMAGE=$backend"
|
||
echo "CHATBALLS_FRONTEND_IMAGE=$frontend"
|
||
echo "CHATBALLS_GATEWAY_IMAGE=$gateway"
|
||
echo "CHATBALLS_POSTGRES_IMAGE=$postgres"
|
||
echo "CHATBALLS_REDIS_IMAGE=$redis"
|
||
echo "CHATBALLS_COTURN_IMAGE=$coturn"
|
||
} > dist/release.env
|
||
|
||
# Файл, который скачает человек, обязан быть валидным сам по себе —
|
||
# без переменных окружения и без чего-либо рядом.
|
||
( cd dist && docker compose -f compose.yaml config -q )
|
||
|
||
cat dist/compose.yaml
|
||
|
||
- uses: actions/upload-artifact@v4
|
||
with:
|
||
name: release-compose
|
||
path: |
|
||
dist/compose.yaml
|
||
dist/release.env
|
||
|
||
- name: Приложить к релизу
|
||
if: startsWith(github.ref, 'refs/tags/')
|
||
uses: softprops/action-gh-release@v2
|
||
with:
|
||
files: |
|
||
dist/compose.yaml
|
||
dist/release.env
|