feat:接入 Gitea 离线发版链路

This commit is contained in:
LoveLosita
2026-05-09 14:29:35 +08:00
parent cc98b62ad8
commit 9025096bc6
11 changed files with 666 additions and 1 deletions

View File

@@ -0,0 +1,101 @@
name: offline-release
on:
workflow_dispatch:
inputs:
base_ref:
description: "可选:用于 impact diff 的起始 ref留空则默认 HEAD^"
required: false
include_infra:
description: "是否同时打 infra bundle"
required: false
default: "false"
jobs:
package-and-deploy:
runs-on: build-host
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Resolve release refs
shell: bash
run: |
set -euo pipefail
APP_TAG="$(git rev-parse --short=12 HEAD)"
BASE_REF="${{ inputs.base_ref }}"
if [[ -z "${BASE_REF}" ]] && git rev-parse --verify --quiet HEAD^ >/dev/null; then
BASE_REF="$(git rev-parse HEAD^)"
fi
{
echo "APP_TAG=${APP_TAG}"
echo "BASE_REF=${BASE_REF}"
} >> "${GITHUB_ENV}"
- name: Build release plan
shell: bash
run: |
set -euo pipefail
./deploy/impact-rules.sh "${BASE_REF:-}" HEAD deploy/release-plan.env
cat deploy/release-plan.env
- name: Pack docker images
shell: bash
run: |
set -euo pipefail
source deploy/release-plan.env
args=(--app-tag "${APP_TAG}")
if [[ "${SMARTFLOW_BUILD_BACKEND}" != "1" ]]; then
args+=(--skip-backend)
fi
if [[ "${SMARTFLOW_BUILD_FRONTEND}" != "1" ]]; then
args+=(--skip-frontend)
fi
if [[ "${{ inputs.include_infra }}" == "true" ]]; then
args+=(--include-infra)
fi
./deploy/docker-pack.sh "${args[@]}"
- name: Stage release directory
shell: bash
run: |
set -euo pipefail
./deploy/stage-release.sh \
--release-dir ".release/${APP_TAG}" \
--plan-file "deploy/release-plan.env" \
--bundle-dir ".docker-bundles"
- name: Upload release bundle
shell: bash
env:
SMARTFLOW_DEPLOY_HOST: ${{ secrets.SMARTFLOW_DEPLOY_HOST }}
SMARTFLOW_DEPLOY_PORT: ${{ secrets.SMARTFLOW_DEPLOY_PORT }}
SMARTFLOW_DEPLOY_USER: ${{ secrets.SMARTFLOW_DEPLOY_USER }}
SMARTFLOW_DEPLOY_SSH_KEY: ${{ secrets.SMARTFLOW_DEPLOY_SSH_KEY }}
run: |
set -euo pipefail
mkdir -p ~/.ssh
printf '%s\n' "${SMARTFLOW_DEPLOY_SSH_KEY}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -p "${SMARTFLOW_DEPLOY_PORT:-22}" "${SMARTFLOW_DEPLOY_HOST}" >> ~/.ssh/known_hosts
tar -C ".release/${APP_TAG}" -czf ".release/${APP_TAG}.tgz" .
ssh -p "${SMARTFLOW_DEPLOY_PORT:-22}" "${SMARTFLOW_DEPLOY_USER}@${SMARTFLOW_DEPLOY_HOST}" "mkdir -p /srv/smartflow/releases/${APP_TAG}"
scp -P "${SMARTFLOW_DEPLOY_PORT:-22}" ".release/${APP_TAG}.tgz" "${SMARTFLOW_DEPLOY_USER}@${SMARTFLOW_DEPLOY_HOST}:/srv/smartflow/releases/${APP_TAG}.tgz"
ssh -p "${SMARTFLOW_DEPLOY_PORT:-22}" "${SMARTFLOW_DEPLOY_USER}@${SMARTFLOW_DEPLOY_HOST}" "rm -rf /srv/smartflow/releases/${APP_TAG}/* && tar -xzf /srv/smartflow/releases/${APP_TAG}.tgz -C /srv/smartflow/releases/${APP_TAG} && rm -f /srv/smartflow/releases/${APP_TAG}.tgz"
- name: Trigger deploy
shell: bash
env:
SMARTFLOW_DEPLOY_HOST: ${{ secrets.SMARTFLOW_DEPLOY_HOST }}
SMARTFLOW_DEPLOY_PORT: ${{ secrets.SMARTFLOW_DEPLOY_PORT }}
SMARTFLOW_DEPLOY_USER: ${{ secrets.SMARTFLOW_DEPLOY_USER }}
SMARTFLOW_DEPLOY_SSH_KEY: ${{ secrets.SMARTFLOW_DEPLOY_SSH_KEY }}
run: |
set -euo pipefail
mkdir -p ~/.ssh
printf '%s\n' "${SMARTFLOW_DEPLOY_SSH_KEY}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -p "${SMARTFLOW_DEPLOY_PORT:-22}" "${SMARTFLOW_DEPLOY_HOST}" >> ~/.ssh/known_hosts
ssh -p "${SMARTFLOW_DEPLOY_PORT:-22}" "${SMARTFLOW_DEPLOY_USER}@${SMARTFLOW_DEPLOY_HOST}" "smartflow-release deploy ${APP_TAG}"

2
.gitignore vendored
View File

@@ -34,3 +34,5 @@ backend/config.yaml
/backend/.dev/
/.docker-bundles/
.gopath/
/deploy/release-plan.env
/.release/

View File

@@ -4,12 +4,15 @@
- `tls.crt`:证书链文件(如 `fullchain.pem` / `bundle.pem`
- `tls.key`:私钥文件
- `git.lecspace.com_bundle.pem`Gitea 域名 `git.lecspace.com` 的证书链
- `git.lecspace.com.key`Gitea 域名 `git.lecspace.com` 的私钥
`docker-compose.full.yml` 会把本目录挂载到前端容器的 `/etc/nginx/certs`
`frontend/nginx.conf` 也会固定从该路径读取这两个文件。
`frontend/nginx.conf``deploy/nginx/default.conf` 也会固定从该路径读取这些证书文件。
注意:
1. 证书和私钥属于敏感文件,不要提交到仓库。
2. 若证书文件名不同,请在部署前重命名为上面的通用名称。
3. 更新证书后,只需要重建或重启 `frontend` 容器即可生效。
4. 如果暂时不接入 Gitea 域名,可先不放 `git.lecspace.com_bundle.pem``git.lecspace.com.key`,但对应站点会无法完成 HTTPS 代理。

113
deploy/docker-pack.sh Executable file
View File

@@ -0,0 +1,113 @@
#!/usr/bin/env bash
set -euo pipefail
APP_TAG="latest"
BACKEND_IMAGE="smartflow/backend-suite"
FRONTEND_IMAGE="smartflow/frontend"
OUTPUT_DIR=".docker-bundles"
INCLUDE_INFRA=0
SKIP_BACKEND=0
SKIP_FRONTEND=0
while [[ $# -gt 0 ]]; do
case "$1" in
--app-tag)
APP_TAG="$2"
shift 2
;;
--backend-image)
BACKEND_IMAGE="$2"
shift 2
;;
--frontend-image)
FRONTEND_IMAGE="$2"
shift 2
;;
--output-dir)
OUTPUT_DIR="$2"
shift 2
;;
--include-infra)
INCLUDE_INFRA=1
shift
;;
--skip-backend)
SKIP_BACKEND=1
shift
;;
--skip-frontend)
SKIP_FRONTEND=1
shift
;;
*)
echo "unknown argument: $1" >&2
exit 64
;;
esac
done
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
bundle_dir="${repo_root}/${OUTPUT_DIR}"
backend_ref="${BACKEND_IMAGE}:${APP_TAG}"
frontend_ref="${FRONTEND_IMAGE}:${APP_TAG}"
app_bundle_path="${bundle_dir}/smartflow-app-${APP_TAG}.tar"
infra_bundle_path="${bundle_dir}/smartflow-infra-${APP_TAG}.tar"
mkdir -p "${bundle_dir}"
if [[ "${SKIP_BACKEND}" -eq 0 ]]; then
echo "==> Build backend image ${backend_ref}"
docker build --platform linux/amd64 -f "${repo_root}/backend/Dockerfile" -t "${backend_ref}" "${repo_root}/backend"
fi
if [[ "${SKIP_FRONTEND}" -eq 0 ]]; then
echo "==> Build frontend image ${frontend_ref}"
docker build --platform linux/amd64 -f "${repo_root}/frontend/Dockerfile" -t "${frontend_ref}" "${repo_root}/frontend"
fi
declare -a app_images=()
if [[ "${SKIP_BACKEND}" -eq 0 ]]; then
app_images+=("${backend_ref}")
fi
if [[ "${SKIP_FRONTEND}" -eq 0 ]]; then
app_images+=("${frontend_ref}")
fi
if [[ "${#app_images[@]}" -gt 0 ]]; then
rm -f "${app_bundle_path}"
echo "==> Export app bundle to ${app_bundle_path}"
docker save -o "${app_bundle_path}" "${app_images[@]}"
else
echo "==> Skip app bundle export because backend/frontend are both skipped"
fi
if [[ "${INCLUDE_INFRA}" -eq 0 ]]; then
echo "==> Done."
exit 0
fi
infra_images=(
"${SMARTFLOW_MYSQL_IMAGE:-mysql:8.0}"
"${SMARTFLOW_REDIS_IMAGE:-redis:7}"
"${SMARTFLOW_KAFKA_IMAGE:-apache/kafka:3.7.2}"
"${SMARTFLOW_ETCD_IMAGE:-quay.io/coreos/etcd:v3.5.5}"
"${SMARTFLOW_MINIO_IMAGE:-minio/minio:RELEASE.2023-03-20T20-16-18Z}"
"${SMARTFLOW_MILVUS_IMAGE:-milvusdb/milvus:v2.4.4}"
"${SMARTFLOW_ATTU_IMAGE:-zilliz/attu:v2.4.3}"
)
for image_ref in "${infra_images[@]}"; do
if docker image inspect "${image_ref}" >/dev/null 2>&1; then
echo "==> Reuse local infra image ${image_ref}"
continue
fi
echo "==> Pull infra image ${image_ref}"
docker pull "${image_ref}"
done
rm -f "${infra_bundle_path}"
echo "==> Export infra bundle to ${infra_bundle_path}"
docker save -o "${infra_bundle_path}" "${infra_images[@]}"
echo "==> Done."

153
deploy/impact-rules.sh Executable file
View File

@@ -0,0 +1,153 @@
#!/usr/bin/env bash
set -euo pipefail
base_ref="${1:-}"
head_ref="${2:-HEAD}"
output_file="${3:-}"
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "${repo_root}"
backend_services=(userauth notification active-scheduler schedule task task-class course memory agent taskclassforum tokenstore llm api)
selected_services=()
frontend_changed=0
full_backend=0
add_service() {
local service="$1"
for current in "${selected_services[@]}"; do
if [[ "${current}" == "${service}" ]]; then
return 0
fi
done
selected_services+=("${service}")
}
have_ref() {
local ref="$1"
[[ -n "${ref}" ]] && git rev-parse --verify --quiet "${ref}^{commit}" >/dev/null
}
if ! have_ref "${head_ref}"; then
echo "head ref not found: ${head_ref}" >&2
exit 65
fi
app_tag="$(git rev-parse --short=12 "${head_ref}")"
declare -a changed_files=()
if have_ref "${base_ref}"; then
mapfile -t changed_files < <(git diff --name-only "${base_ref}" "${head_ref}")
else
frontend_changed=1
full_backend=1
fi
for file in "${changed_files[@]}"; do
case "${file}" in
README.md|docs/*)
;;
frontend/*|deploy/nginx/*)
frontend_changed=1
;;
deploy/docker-pack.*|deploy/docker-load.sh|deploy/stage-release.sh|deploy/project-release.sh|deploy/project-rollback.sh|deploy/impact-rules.sh)
frontend_changed=1
full_backend=1
;;
docker-compose.full.yml|frontend/nginx.conf|.env.full.example)
frontend_changed=1
full_backend=1
;;
backend/Dockerfile|backend/config.docker.yaml|backend/shared/*|backend/client/*)
full_backend=1
;;
backend/gateway/*|backend/cmd/api/*)
add_service "api"
;;
backend/cmd/userauth/*|backend/services/userauth/*)
add_service "userauth"
;;
backend/cmd/notification/*|backend/services/notification/*)
add_service "notification"
;;
backend/cmd/active-scheduler/*|backend/services/active_scheduler/*)
add_service "active-scheduler"
;;
backend/cmd/schedule/*|backend/services/schedule/*)
add_service "schedule"
;;
backend/cmd/task/*|backend/services/task/*)
add_service "task"
;;
backend/cmd/task-class/*|backend/services/task_class/*)
add_service "task-class"
;;
backend/cmd/course/*|backend/services/course/*)
add_service "course"
;;
backend/cmd/memory/*|backend/services/memory/*)
add_service "memory"
;;
backend/cmd/agent/*|backend/services/agent/*)
add_service "agent"
;;
backend/cmd/taskclassforum/*|backend/services/taskclassforum/*)
add_service "taskclassforum"
;;
backend/cmd/tokenstore/*|backend/services/tokenstore/*)
add_service "tokenstore"
;;
backend/cmd/llm/*|backend/services/llm/*)
add_service "llm"
;;
backend/*)
full_backend=1
;;
esac
done
if [[ "${full_backend}" -eq 1 ]]; then
selected_services=("${backend_services[@]}")
fi
if [[ "${frontend_changed}" -eq 1 ]]; then
add_service "frontend"
fi
build_backend=0
build_frontend=0
for service in "${selected_services[@]}"; do
if [[ "${service}" == "frontend" ]]; then
build_frontend=1
else
build_backend=1
fi
done
noop=0
if [[ "${#selected_services[@]}" -eq 0 ]]; then
noop=1
fi
restart_csv=""
if [[ "${#selected_services[@]}" -gt 0 ]]; then
restart_csv="$(IFS=,; echo "${selected_services[*]}")"
fi
content=$(
cat <<EOF
SMARTFLOW_APP_TAG=${app_tag}
SMARTFLOW_NOOP=${noop}
SMARTFLOW_BUILD_BACKEND=${build_backend}
SMARTFLOW_BUILD_FRONTEND=${build_frontend}
SMARTFLOW_BACKEND_IMAGE=smartflow/backend-suite:${app_tag}
SMARTFLOW_FRONTEND_IMAGE=smartflow/frontend:${app_tag}
SMARTFLOW_RESTART_SERVICES=${restart_csv}
EOF
)
if [[ -n "${output_file}" ]]; then
printf '%s\n' "${content}" > "${output_file}"
else
printf '%s\n' "${content}"
fi

86
deploy/nginx/default.conf Normal file
View File

@@ -0,0 +1,86 @@
server {
listen 80;
server_name smartmate.lecspace.com _;
return 301 https://$host$request_uri;
}
server {
listen 80;
server_name git.lecspace.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
server_name smartmate.lecspace.com;
resolver 127.0.0.11 ipv6=off valid=30s;
ssl_certificate /etc/nginx/certs/tls.crt;
ssl_certificate_key /etc/nginx/certs/tls.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
root /usr/share/nginx/html;
index index.html;
# 1. 生产环境统一由前端容器反代后端 API前端继续使用相对路径 /api/v1。
# 2. 关闭代理缓冲,避免 Agent SSE 响应被 Nginx 缓存后前端长时间收不到数据。
location /api/ {
set $api_upstream http://api:8080;
proxy_pass $api_upstream;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 3600s;
add_header X-Accel-Buffering no;
}
# 1. Vue Router 走 history 模式时,静态资源未命中需要回落到 index.html。
# 2. 这里只负责前端页面回落,接口流量统一由上面的 /api 路由处理。
location / {
try_files $uri $uri/ /index.html;
}
}
server {
listen 443 ssl;
http2 on;
server_name git.lecspace.com;
resolver 127.0.0.11 ipv6=off valid=30s;
ssl_certificate /etc/nginx/certs/git.lecspace.com_bundle.pem;
ssl_certificate_key /etc/nginx/certs/git.lecspace.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
client_max_body_size 2g;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
proxy_request_buffering off;
proxy_buffering off;
# 1. Gitea Web、Container Registry 与 API 统一复用同一域名入口。
# 2. 这里改成变量代理,避免 Nginx 启动时强制解析上游而影响离线语法校验。
location / {
set $gitea_upstream http://gitea-web:3000;
proxy_pass $gitea_upstream;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header Connection "";
}
}

104
deploy/project-release.sh Executable file
View File

@@ -0,0 +1,104 @@
#!/usr/bin/env bash
set -euo pipefail
# 1. 只负责把当前 release 目录里的编排资产和镜像包应用到运行目录。
# 2. 不负责生成镜像,也不负责计算影响范围;这些工作由构建机和 impact-rules.sh 提前完成。
# 3. 如果 release-plan.env 缺失或格式错误,直接失败,避免在生产机上猜测要重启哪些服务。
runtime_dir="${SMARTFLOW_RUNTIME_DIR:-/root/smartflow}"
release_dir="${SMARTFLOW_RELEASE_DIR:?SMARTFLOW_RELEASE_DIR is required}"
release_id="${SMARTFLOW_RELEASE_ID:?SMARTFLOW_RELEASE_ID is required}"
plan_file="${release_dir}/deploy/release-plan.env"
runtime_env="${runtime_dir}/.env"
if [[ ! -f "${plan_file}" ]]; then
echo "release plan not found: ${plan_file}" >&2
exit 66
fi
if [[ ! -f "${runtime_env}" ]]; then
echo "runtime env not found: ${runtime_env}" >&2
exit 67
fi
source "${plan_file}"
if [[ "${SMARTFLOW_NOOP:-0}" == "1" ]]; then
echo "release ${release_id} resolved to no-op"
exit 0
fi
set_env_var() {
local key="$1"
local value="$2"
local file="$3"
local tmp
tmp="$(mktemp)"
awk -v key="${key}" -v value="${value}" '
BEGIN { updated = 0 }
$0 ~ ("^" key "=") {
print key "=" value
updated = 1
next
}
{ print }
END {
if (updated == 0) {
print key "=" value
}
}
' "${file}" > "${tmp}"
mv "${tmp}" "${file}"
}
mkdir -p "${runtime_dir}/deploy/nginx"
install -m 0644 "${release_dir}/docker-compose.full.yml" "${runtime_dir}/docker-compose.full.yml"
install -m 0644 "${release_dir}/deploy/nginx/default.conf" "${runtime_dir}/deploy/nginx/default.conf"
if compgen -G "${release_dir}/.docker-bundles/*.tar" >/dev/null 2>&1; then
bash "${release_dir}/deploy/docker-load.sh" "${release_dir}/.docker-bundles"
fi
if [[ "${SMARTFLOW_BUILD_BACKEND:-0}" == "1" ]]; then
set_env_var "SMARTFLOW_BACKEND_IMAGE" "${SMARTFLOW_BACKEND_IMAGE}" "${runtime_env}"
fi
if [[ "${SMARTFLOW_BUILD_FRONTEND:-0}" == "1" ]]; then
set_env_var "SMARTFLOW_FRONTEND_IMAGE" "${SMARTFLOW_FRONTEND_IMAGE}" "${runtime_env}"
fi
services=()
IFS=',' read -r -a raw_services <<< "${SMARTFLOW_RESTART_SERVICES:-}"
for service in "${raw_services[@]}"; do
if [[ -n "${service}" ]]; then
services+=("${service}")
fi
done
if [[ "${#services[@]}" -eq 0 ]]; then
echo "release ${release_id} has no target services"
exit 0
fi
# 1. 使用 --no-deps 只重建命中的服务,避免后端小改动把整套依赖链一起拉起来。
# 2. 如果后续服务新增或删减,只要 release-plan.env 给出的服务名同步更新,这里无需改脚本。
# 3. 失败时直接退出,由上层薄封装决定是否切回旧 release。
(
cd "${runtime_dir}"
docker compose -f docker-compose.full.yml up -d --no-deps "${services[@]}"
)
if [[ "${SMARTFLOW_BUILD_BACKEND:-0}" == "1" || " ${services[*]} " == *" api "* ]]; then
curl -fsS http://127.0.0.1:8080/api/v1/health >/dev/null
fi
if [[ " ${services[*]} " == *" frontend "* ]]; then
curl -k -fsS https://smartmate.lecspace.com/ >/dev/null
curl -k -fsS https://git.lecspace.com/ >/dev/null
fi
printf '%s\n' "${release_id}" > "${SMARTFLOW_RELEASE_ROOT}/last_successful_release"
ln -sfn "${release_dir}" "${SMARTFLOW_CURRENT_LINK}"
echo "release ${release_id} deployed successfully"

9
deploy/project-rollback.sh Executable file
View File

@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
# 1. 回滚本质上就是把目标旧 release 再部署一遍。
# 2. 这里不单独复制逻辑,避免发布和回滚两套脚本漂移。
# 3. 薄封装脚本传入哪个 release_id这里就把哪个 release 当成目标版本重放。
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
exec "${script_dir}/project-release.sh" "$@"

52
deploy/stage-release.sh Executable file
View File

@@ -0,0 +1,52 @@
#!/usr/bin/env bash
set -euo pipefail
release_dir=""
plan_file=""
bundle_dir=".docker-bundles"
while [[ $# -gt 0 ]]; do
case "$1" in
--release-dir)
release_dir="$2"
shift 2
;;
--plan-file)
plan_file="$2"
shift 2
;;
--bundle-dir)
bundle_dir="$2"
shift 2
;;
*)
echo "unknown argument: $1" >&2
exit 64
;;
esac
done
if [[ -z "${release_dir}" || -z "${plan_file}" ]]; then
echo "usage: stage-release.sh --release-dir <dir> --plan-file <file> [--bundle-dir <dir>]" >&2
exit 64
fi
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
release_abs="${repo_root}/${release_dir}"
bundle_abs="${repo_root}/${bundle_dir}"
rm -rf "${release_abs}"
mkdir -p "${release_abs}/deploy/nginx" "${release_abs}/deploy/certs" "${release_abs}/.docker-bundles"
cp "${repo_root}/docker-compose.full.yml" "${release_abs}/docker-compose.full.yml"
cp "${repo_root}/deploy/docker-load.sh" "${release_abs}/deploy/docker-load.sh"
cp "${repo_root}/deploy/project-release.sh" "${release_abs}/deploy/project-release.sh"
cp "${repo_root}/deploy/project-rollback.sh" "${release_abs}/deploy/project-rollback.sh"
cp "${repo_root}/deploy/impact-rules.sh" "${release_abs}/deploy/impact-rules.sh"
cp "${repo_root}/deploy/nginx/default.conf" "${release_abs}/deploy/nginx/default.conf"
cp "${repo_root}/deploy/certs/README.md" "${release_abs}/deploy/certs/README.md"
cp "${plan_file}" "${release_abs}/deploy/release-plan.env"
if compgen -G "${bundle_abs}/*.tar" >/dev/null 2>&1; then
cp "${bundle_abs}"/*.tar "${release_abs}/.docker-bundles/"
fi

View File

@@ -293,6 +293,7 @@ services:
- "${SMARTFLOW_FRONTEND_HTTPS_PORT:-443}:443"
volumes:
- ./deploy/certs:/etc/nginx/certs:ro
- ./deploy/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
api:
condition: service_started

View File

@@ -4,6 +4,12 @@ server {
return 301 https://$host$request_uri;
}
server {
listen 80;
server_name git.lecspace.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
http2 on;
@@ -43,3 +49,38 @@ server {
try_files $uri $uri/ /index.html;
}
}
server {
listen 443 ssl;
http2 on;
server_name git.lecspace.com;
resolver 127.0.0.11 ipv6=off valid=30s;
ssl_certificate /etc/nginx/certs/git.lecspace.com_bundle.pem;
ssl_certificate_key /etc/nginx/certs/git.lecspace.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
client_max_body_size 2g;
proxy_read_timeout 600s;
proxy_send_timeout 600s;
proxy_request_buffering off;
proxy_buffering off;
# 1. Gitea Web、Container Registry 与 API 统一复用同一域名入口。
# 2. 这里改成变量代理,避免 Nginx 启动时强制解析上游而影响离线语法校验。
location / {
set $gitea_upstream http://gitea-web:3000;
proxy_pass $gitea_upstream;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header Connection "";
}
}