Add scheduler backend image workflow
All checks were successful
Build scheduler-backend image / build-and-push (push) Successful in 3m36s

This commit is contained in:
Developer
2026-05-28 13:15:30 +07:00
parent 5f93d025d2
commit 433fb60f0a
5 changed files with 356 additions and 0 deletions

11
.dockerignore Normal file
View File

@@ -0,0 +1,11 @@
.git
.gitea
logs
run
*.exe
*.test
*.out
api
worker
seed-sample-job

View File

@@ -0,0 +1,154 @@
#!/usr/bin/env bash
set -euo pipefail
: "${CONFIG_CENTER_UPDATES_FILE:?CONFIG_CENTER_UPDATES_FILE is required}"
CONFIG_CENTER_CLUSTERS="${CONFIG_CENTER_CLUSTERS:-ack-dev}"
if [ ! -s "$CONFIG_CENTER_UPDATES_FILE" ]; then
echo "No config-center image updates were recorded."
exit 0
fi
if command -v python3 >/dev/null 2>&1; then
PYTHON=python3
elif command -v python >/dev/null 2>&1; then
PYTHON=python
else
echo "Python is required for direct config-center update."
exit 1
fi
WORKDIR="$(mktemp -d)"
trap 'rm -rf "$WORKDIR"' EXIT
echo "Updating config-center after image build succeeded."
git clone git@git.imall.cloud:im-group/config-center.git "$WORKDIR/config-center"
cd "$WORKDIR/config-center"
"$PYTHON" - "$CONFIG_CENTER_UPDATES_FILE" "$CONFIG_CENTER_CLUSTERS" <<'PY'
from __future__ import annotations
import sys
from pathlib import Path
updates_file = Path(sys.argv[1])
clusters = sys.argv[2].split()
updates = [
tuple(line.split("|"))
for line in updates_file.read_text(encoding="utf-8").splitlines()
if line.strip()
]
def update_file(path: Path, deployment: str, image: str, container: str):
lines = path.read_text(encoding="utf-8").splitlines(keepends=True)
changed = False
matched = False
def indent_of(line: str) -> int:
return len(line) - len(line.lstrip(" "))
doc_starts = [
idx for idx, line in enumerate(lines) if line.lstrip(" ").startswith("- apiVersion:")
]
doc_starts.append(len(lines))
for pos, start in enumerate(doc_starts[:-1]):
end = doc_starts[pos + 1]
block = lines[start:end]
if not any(line.lstrip(" ").strip() == "kind: Deployment" for line in block):
continue
metadata_idx = next(
(idx for idx, line in enumerate(block) if line.lstrip(" ").startswith("metadata:")),
None,
)
if metadata_idx is None:
continue
metadata_indent = indent_of(block[metadata_idx])
next_peer = next(
(
idx
for idx in range(metadata_idx + 1, len(block))
if block[idx].strip() and indent_of(block[idx]) <= metadata_indent
),
len(block),
)
metadata = block[metadata_idx:next_peer]
if not any(
line.lstrip(" ").startswith("name: ")
and line.split(":", 1)[1].strip() == deployment
for line in metadata
):
continue
list_starts = []
for idx, line in enumerate(block):
stripped = line.lstrip(" ")
if stripped.startswith(("- image:", "- command:", "- name:")):
list_starts.append(idx)
list_starts.append(len(block))
for item_pos, item_start in enumerate(list_starts[:-1]):
item_end = list_starts[item_pos + 1]
item = block[item_start:item_end]
if container and not any(
line.lstrip(" ").startswith("name: ")
and line.split(":", 1)[1].strip() == container
for line in item
):
continue
for rel_idx, line in enumerate(item):
stripped = line.lstrip(" ")
if stripped.startswith("image:") or stripped.startswith("- image:"):
matched = True
abs_idx = start + item_start + rel_idx
old_line = lines[abs_idx]
indent = indent_of(old_line)
newline = "\n" if old_line.endswith("\n") else ""
prefix = old_line[:indent]
marker = "- " if old_line.lstrip(" ").startswith("- image:") else ""
lines[abs_idx] = f"{prefix}{marker}image: {image}{newline}"
changed = changed or lines[abs_idx] != old_line
break
if matched:
break
if matched:
break
if changed:
path.write_text("".join(lines), encoding="utf-8")
return matched, changed
matched_any = False
changed_any = False
for cluster in clusters:
file = Path("apps/openim/overlays") / cluster / "deployments.yaml"
if not file.exists():
raise SystemExit(f"{file} does not exist")
for deployment, image, container in updates:
matched, changed = update_file(file, deployment, image, container)
status = "updated" if changed else "already current" if matched else "no matching change"
print(f"{cluster}: {deployment} -> {image}: {status}")
matched_any = matched_any or matched
changed_any = changed_any or changed
if not matched_any:
raise SystemExit("no config-center deployment/container matches were found")
if not changed_any:
print("All matching config-center images are already current.")
PY
if git diff --quiet -- apps/openim/overlays; then
echo "No config-center image changes to commit."
exit 0
fi
git config user.name "gitea-actions"
git config user.email "gitea-actions@git.imall.cloud"
git add apps/openim/overlays/*/deployments.yaml
git commit -m "Deploy scheduler-backend image after successful build"
git push origin main

View File

@@ -0,0 +1,120 @@
name: Build scheduler-backend image
on:
push:
branches: [dev, main]
paths:
- "**"
workflow_dispatch:
inputs:
tag:
description: "Optional image tag. Defaults to <branch>-<short-sha>."
required: false
default: ""
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
env:
REGISTRY: docker.io
DOCKER_USER: ${{ secrets.DOCKER_USERNAME || 'mag1666888' }}
SERVICE_NAME: scheduler-backend
DEPLOYMENT_NAME: scheduler-backend
CONFIG_CENTER_CONTAINER: scheduler-backend
DOCKERFILE: Dockerfile
INPUT_TAG: ${{ github.event.inputs.tag }}
jobs:
build-and-push:
runs-on: [openim, config-center]
steps:
- name: Configure SSH
env:
GIT_SSH_PRIVATE_KEY: ${{ secrets.GIT_SSH_PRIVATE_KEY }}
run: |
set -euo pipefail
if [ -z "${GIT_SSH_PRIVATE_KEY:-}" ]; then
echo "GIT_SSH_PRIVATE_KEY is required."
exit 1
fi
mkdir -p ~/.ssh
chmod 700 ~/.ssh
if printf '%s' "$GIT_SSH_PRIVATE_KEY" | grep -q "BEGIN .*PRIVATE KEY"; then
printf '%s\n' "$GIT_SSH_PRIVATE_KEY" > ~/.ssh/id_rsa
else
printf '%s' "$GIT_SSH_PRIVATE_KEY" | base64 -d > ~/.ssh/id_rsa
fi
chmod 600 ~/.ssh/id_rsa
ssh-keyscan -H git.imall.cloud >> ~/.ssh/known_hosts 2>/dev/null || true
chmod 644 ~/.ssh/known_hosts
ssh -o BatchMode=yes -o StrictHostKeyChecking=yes -T git@git.imall.cloud || true
- name: Checkout
run: |
set -euo pipefail
BRANCH_NAME="${GITHUB_REF_NAME:-${GITEA_REF_NAME:-main}}"
COMMIT_SHA="${GITHUB_SHA:-${GITEA_SHA:-}}"
git init .
git remote remove origin 2>/dev/null || true
git remote add origin git@git.imall.cloud:im-group/scheduler-backend.git
if [ -n "$COMMIT_SHA" ]; then
git fetch --depth 1 origin "$COMMIT_SHA"
fi
if [ -z "$COMMIT_SHA" ]; then
git fetch --depth 1 origin "$BRANCH_NAME"
fi
git checkout --force FETCH_HEAD
- name: Login to Docker Hub
uses: docker/login-action@v3.3.0
with:
registry: ${{ env.REGISTRY }}
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.8.0
- name: Test
run: |
set -euo pipefail
docker build \
--target test \
-f "$DOCKERFILE" \
.
- name: Build and push image
run: |
set -euo pipefail
SHORT_SHA="$(git rev-parse --short=7 HEAD)"
BRANCH_NAME="${GITHUB_REF_NAME:-${GITEA_REF_NAME:-$(git rev-parse --abbrev-ref HEAD)}}"
BRANCH_TAG="$(printf '%s' "$BRANCH_NAME" | sed 's/[^a-zA-Z0-9-]/-/g' | tr '[:upper:]' '[:lower:]')"
if [ -n "${INPUT_TAG:-}" ]; then
VERSION_TAG="$INPUT_TAG"
else
VERSION_TAG="${BRANCH_TAG}-${SHORT_SHA}"
fi
IMAGE="${DOCKER_USER}/${SERVICE_NAME}:${VERSION_TAG}"
docker buildx build \
--platform linux/amd64 \
-f "$DOCKERFILE" \
-t "$IMAGE" \
--push \
.
echo "Built and pushed $IMAGE for deployment $DEPLOYMENT_NAME"
CONFIG_CENTER_UPDATES_FILE="$(mktemp)"
export CONFIG_CENTER_UPDATES_FILE
printf '%s|%s|%s\n' "$DEPLOYMENT_NAME" "$IMAGE" "$CONFIG_CENTER_CONTAINER" > "$CONFIG_CENTER_UPDATES_FILE"
CONFIG_CENTER_CLUSTERS="${CONFIG_CENTER_CLUSTERS:-ack-dev}" \
bash .gitea/scripts/update-config-center-images.sh

40
Dockerfile Normal file
View File

@@ -0,0 +1,40 @@
FROM golang:1.24-alpine AS base
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
FROM base AS test
RUN go test ./...
FROM base AS builder
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /out/api ./cmd/api \
&& CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /out/worker ./cmd/worker \
&& CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o /out/seed-sample-job ./cmd/tools/seed-sample-job
FROM alpine:3.20
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --from=builder /out/api /app/api
COPY --from=builder /out/worker /app/worker
COPY --from=builder /out/seed-sample-job /app/seed-sample-job
COPY job-config-list /app/job-config-list
COPY docker-entrypoint.sh /app/docker-entrypoint.sh
RUN chmod +x /app/api /app/worker /app/seed-sample-job /app/docker-entrypoint.sh \
&& chown -R app:app /app
USER app
ENV HTTP_PORT=16811 \
WORKER_HTTP_PORT=16812
EXPOSE 16811 16812
ENTRYPOINT ["/app/docker-entrypoint.sh"]

31
docker-entrypoint.sh Normal file
View File

@@ -0,0 +1,31 @@
#!/bin/sh
set -eu
/app/api &
api_pid="$!"
/app/worker &
worker_pid="$!"
term() {
kill "$api_pid" "$worker_pid" 2>/dev/null || true
wait "$api_pid" "$worker_pid" 2>/dev/null || true
}
trap term INT TERM
while :; do
if ! kill -0 "$api_pid" 2>/dev/null; then
wait "$api_pid" || true
term
exit 1
fi
if ! kill -0 "$worker_pid" 2>/dev/null; then
wait "$worker_pid" || true
term
exit 1
fi
sleep 2
done