name: docker-images on: push: branches: - main - master paths: - backend/** - frontend/** - admin/** - deploy/docker/** - .gitea/workflows/backend-docker.yml workflow_dispatch: permissions: contents: read packages: write jobs: build-and-push: runs-on: ubuntu-latest strategy: fail-fast: false matrix: include: - component: backend dockerfile: backend/Dockerfile context: backend default_image_name: termi-astro-backend - component: frontend dockerfile: frontend/Dockerfile context: frontend default_image_name: termi-astro-frontend - component: admin dockerfile: admin/Dockerfile context: admin default_image_name: termi-astro-admin steps: - name: Checkout uses: actions/checkout@v4 - name: Resolve image metadata id: meta shell: bash env: COMPONENT: ${{ matrix.component }} DEFAULT_IMAGE_NAME: ${{ matrix.default_image_name }} VAR_REGISTRY_HOST: ${{ vars.REGISTRY_HOST }} VAR_IMAGE_NAMESPACE: ${{ vars.IMAGE_NAMESPACE }} VAR_IMAGE_NAME: ${{ vars.IMAGE_NAME }} VAR_BACKEND_IMAGE_NAME: ${{ vars.BACKEND_IMAGE_NAME }} VAR_FRONTEND_IMAGE_NAME: ${{ vars.FRONTEND_IMAGE_NAME }} VAR_ADMIN_IMAGE_NAME: ${{ vars.ADMIN_IMAGE_NAME }} VAR_FRONTEND_PUBLIC_API_BASE_URL: ${{ vars.FRONTEND_PUBLIC_API_BASE_URL }} VAR_ADMIN_VITE_API_BASE: ${{ vars.ADMIN_VITE_API_BASE }} VAR_ADMIN_VITE_FRONTEND_BASE_URL: ${{ vars.ADMIN_VITE_FRONTEND_BASE_URL }} VAR_ADMIN_VITE_BASENAME: ${{ vars.ADMIN_VITE_BASENAME }} run: | set -euo pipefail REGISTRY_HOST="${VAR_REGISTRY_HOST:-${GITEA_SERVER_URL#https://}}" if [ -z "${REGISTRY_HOST}" ]; then REGISTRY_HOST="git.init.cool" fi REPO_OWNER="${GITHUB_REPOSITORY_OWNER:-${GITEA_REPOSITORY_OWNER:-cool}}" IMAGE_NAMESPACE="${VAR_IMAGE_NAMESPACE:-${REPO_OWNER}}" case "${COMPONENT}" in backend) IMAGE_NAME="${VAR_BACKEND_IMAGE_NAME:-${VAR_IMAGE_NAME:-${DEFAULT_IMAGE_NAME}}}" ;; frontend) IMAGE_NAME="${VAR_FRONTEND_IMAGE_NAME:-${DEFAULT_IMAGE_NAME}}" ;; admin) IMAGE_NAME="${VAR_ADMIN_IMAGE_NAME:-${DEFAULT_IMAGE_NAME}}" ;; *) IMAGE_NAME="${DEFAULT_IMAGE_NAME}" ;; esac REF_NAME="${GITHUB_REF_NAME:-${GITEA_REF_NAME:-main}}" SAFE_REF="$(echo "${REF_NAME}" | tr '[:upper:]' '[:lower:]' | sed 's#[^a-z0-9._-]#-#g')" COMMIT_SHA="${GITHUB_SHA:-${GITEA_SHA:-dev}}" SHORT_SHA="$(echo "${COMMIT_SHA}" | cut -c1-12)" IMAGE_BASE="${REGISTRY_HOST}/${IMAGE_NAMESPACE}/${IMAGE_NAME}" FRONTEND_PUBLIC_API_BASE_URL="${VAR_FRONTEND_PUBLIC_API_BASE_URL:-http://localhost:5150/api}" ADMIN_VITE_API_BASE="${VAR_ADMIN_VITE_API_BASE:-http://localhost:5150}" ADMIN_VITE_FRONTEND_BASE_URL="${VAR_ADMIN_VITE_FRONTEND_BASE_URL:-http://localhost:4321}" ADMIN_VITE_BASENAME="${VAR_ADMIN_VITE_BASENAME:-}" { echo "registry_host=${REGISTRY_HOST}" echo "image_base=${IMAGE_BASE}" echo "tag_latest=latest" echo "tag_branch=${SAFE_REF}" echo "tag_sha=${SHORT_SHA}" echo "frontend_public_api_base_url=${FRONTEND_PUBLIC_API_BASE_URL}" echo "admin_vite_api_base=${ADMIN_VITE_API_BASE}" echo "admin_vite_frontend_base_url=${ADMIN_VITE_FRONTEND_BASE_URL}" echo "admin_vite_basename=${ADMIN_VITE_BASENAME}" } >> "$GITHUB_OUTPUT" - name: Login registry shell: bash env: REGISTRY_HOST: ${{ steps.meta.outputs.registry_host }} REGISTRY_USER: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }} BUILTIN_GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} GITHUB_ACTOR_NAME: ${{ github.actor }} run: | set -euo pipefail CUSTOM_REGISTRY_USER="${REGISTRY_USER:-}" CUSTOM_REGISTRY_TOKEN="${REGISTRY_TOKEN:-}" BUILTIN_REGISTRY_TOKEN="${BUILTIN_GITEA_TOKEN:-}" ACTOR_USER="${GITHUB_ACTOR_NAME:-}" if [ -n "${CUSTOM_REGISTRY_TOKEN}" ]; then REGISTRY_USER="${CUSTOM_REGISTRY_USER:-${ACTOR_USER}}" REGISTRY_TOKEN="${CUSTOM_REGISTRY_TOKEN}" else REGISTRY_USER="${ACTOR_USER:-${CUSTOM_REGISTRY_USER}}" REGISTRY_TOKEN="${BUILTIN_REGISTRY_TOKEN}" fi if [ -z "${REGISTRY_USER}" ] || [ -z "${REGISTRY_TOKEN}" ]; then echo "Missing registry credentials: set REGISTRY_USERNAME/REGISTRY_TOKEN, or rely on the built-in GITEA_TOKEN with packages:write permission." exit 1 fi echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY_HOST}" --username "${REGISTRY_USER}" --password-stdin - name: Build image shell: bash env: COMPONENT: ${{ matrix.component }} DOCKERFILE: ${{ matrix.dockerfile }} CONTEXT_DIR: ${{ matrix.context }} IMAGE_BASE: ${{ steps.meta.outputs.image_base }} TAG_LATEST: ${{ steps.meta.outputs.tag_latest }} TAG_BRANCH: ${{ steps.meta.outputs.tag_branch }} TAG_SHA: ${{ steps.meta.outputs.tag_sha }} FRONTEND_PUBLIC_API_BASE_URL: ${{ steps.meta.outputs.frontend_public_api_base_url }} ADMIN_VITE_API_BASE: ${{ steps.meta.outputs.admin_vite_api_base }} ADMIN_VITE_FRONTEND_BASE_URL: ${{ steps.meta.outputs.admin_vite_frontend_base_url }} ADMIN_VITE_BASENAME: ${{ steps.meta.outputs.admin_vite_basename }} run: | set -euo pipefail BUILD_ARGS=() if [ "${COMPONENT}" = "frontend" ]; then BUILD_ARGS+=(--build-arg "PUBLIC_API_BASE_URL=${FRONTEND_PUBLIC_API_BASE_URL}") fi if [ "${COMPONENT}" = "admin" ]; then BUILD_ARGS+=(--build-arg "VITE_API_BASE=${ADMIN_VITE_API_BASE}") BUILD_ARGS+=(--build-arg "VITE_FRONTEND_BASE_URL=${ADMIN_VITE_FRONTEND_BASE_URL}") BUILD_ARGS+=(--build-arg "VITE_ADMIN_BASENAME=${ADMIN_VITE_BASENAME}") fi docker build \ --file "${DOCKERFILE}" \ "${BUILD_ARGS[@]}" \ --tag "${IMAGE_BASE}:${TAG_LATEST}" \ --tag "${IMAGE_BASE}:${TAG_BRANCH}" \ --tag "${IMAGE_BASE}:${TAG_SHA}" \ "${CONTEXT_DIR}" - name: Push image shell: bash env: IMAGE_BASE: ${{ steps.meta.outputs.image_base }} TAG_LATEST: ${{ steps.meta.outputs.tag_latest }} TAG_BRANCH: ${{ steps.meta.outputs.tag_branch }} TAG_SHA: ${{ steps.meta.outputs.tag_sha }} run: | set -euo pipefail docker push "${IMAGE_BASE}:${TAG_LATEST}" docker push "${IMAGE_BASE}:${TAG_BRANCH}" docker push "${IMAGE_BASE}:${TAG_SHA}" - name: Output image tags shell: bash env: COMPONENT: ${{ matrix.component }} IMAGE_BASE: ${{ steps.meta.outputs.image_base }} TAG_LATEST: ${{ steps.meta.outputs.tag_latest }} TAG_BRANCH: ${{ steps.meta.outputs.tag_branch }} TAG_SHA: ${{ steps.meta.outputs.tag_sha }} run: | echo "[${COMPONENT}] pushed tags:" echo "- ${IMAGE_BASE}:${TAG_LATEST}" echo "- ${IMAGE_BASE}:${TAG_BRANCH}" echo "- ${IMAGE_BASE}:${TAG_SHA}"