- 新增 CI/CD 工作流文件,实现前端项目的自动构建和部署 - 支持 main、master 和 develop 分支的自动构建- 包含代码检出、环境安装、依赖管理、项目构建等步骤 - 实现构建产物的自动部署和 Docker 容器重启
483 lines
20 KiB
YAML
483 lines
20 KiB
YAML
name: Epic UI Build & Deploy
|
||
run-name: ${{ gitea.actor }} 正在构建 Epic UI 前端项目 🚀
|
||
on:
|
||
push:
|
||
branches: [ main, master, develop ]
|
||
pull_request:
|
||
branches: [ main, master ]
|
||
|
||
jobs:
|
||
build:
|
||
runs-on: gitea_labels
|
||
container:
|
||
image: gitea-ci-bash:latest
|
||
env:
|
||
# 指定容器将工具缓存路径存放到 /opt/hostedtoolcache2,该目录是Gitea Runner的标准工具缓存目录
|
||
# RUNNER_TOOL_CACHE: /opt/hostedtoolcache2
|
||
steps:
|
||
- name: 检出代码
|
||
shell: bash
|
||
run: |
|
||
echo "📥 检出代码到工作目录..."
|
||
echo "当前目录: $(pwd)"
|
||
echo "仓库: ${{ gitea.repository }}"
|
||
echo "分支: ${{ gitea.ref }}"
|
||
echo "提交: ${{ gitea.sha }}"
|
||
|
||
# 解析分支名
|
||
BRANCH_NAME=$(echo "${{ gitea.ref }}" | sed 's#refs/heads/##')
|
||
|
||
# 拉取代码(使用token鉴权)
|
||
git clone --depth=1 -b "$BRANCH_NAME" "http://1c18ee1ab9a9cb291506d0c5c016a33be7d59e8c:x-oauth-basic@gitea.htoop.cn/${{ gitea.repository }}.git" .
|
||
if [ $? -ne 0 ]; then
|
||
echo "❌ 代码检出失败"
|
||
exit 1
|
||
fi
|
||
|
||
# 校验分支
|
||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||
if [ "$CURRENT_BRANCH" != "$BRANCH_NAME" ]; then
|
||
echo "❌ 检出分支与预期不符: $CURRENT_BRANCH != $BRANCH_NAME"
|
||
exit 1
|
||
fi
|
||
|
||
# 校验提交
|
||
CURRENT_COMMIT=$(git rev-parse HEAD)
|
||
if [ -n "${{ gitea.sha }}" ] && [ "$CURRENT_COMMIT" != "${{ gitea.sha }}" ]; then
|
||
echo "❌ 检出提交与预期不符: $CURRENT_COMMIT != ${{ gitea.sha }}"
|
||
exit 1
|
||
fi
|
||
|
||
echo "✅ 代码检出成功,分支和提交校验通过"
|
||
ls -la
|
||
|
||
- name: 安装Node.js环境
|
||
shell: bash
|
||
run: |
|
||
echo "🔧 安装Node.js环境..."
|
||
if command -v node &> /dev/null; then
|
||
echo "✅ Node.js已安装: $(node --version)"
|
||
echo "✅ npm已安装: $(npm --version)"
|
||
else
|
||
echo "📥 下载并安装Node.js..."
|
||
NODE_VERSION="18.19.0"
|
||
NODE_ARCH="linux-x64"
|
||
MIRRORS=(
|
||
"https://mirrors.aliyun.com/nodejs-release/v${NODE_VERSION}/node-v${NODE_VERSION}-${NODE_ARCH}.tar.xz"
|
||
"https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-${NODE_ARCH}.tar.xz"
|
||
)
|
||
DOWNLOAD_SUCCESS=false
|
||
for mirror in "${MIRRORS[@]}"; do
|
||
echo "尝试从镜像下载: $mirror"
|
||
if wget -q --timeout=30 --tries=3 "$mirror" -O node.tar.xz; then
|
||
echo "✅ 下载成功: $mirror"
|
||
DOWNLOAD_SUCCESS=true
|
||
break
|
||
else
|
||
echo "❌ 下载失败: $mirror"
|
||
continue
|
||
fi
|
||
done
|
||
if [ "$DOWNLOAD_SUCCESS" = false ]; then
|
||
echo "❌ 所有镜像源下载失败"
|
||
exit 1
|
||
fi
|
||
echo "解压Node.js到/usr/local..."
|
||
tar -C /usr/local -xJf node.tar.xz --strip-components=1
|
||
export PATH=$PATH:/usr/local/bin
|
||
echo "验证Node.js安装..."
|
||
node --version
|
||
npm --version
|
||
rm node.tar.xz
|
||
echo "✅ Node.js安装完成"
|
||
fi
|
||
|
||
- name: 检查Node.js环境
|
||
shell: bash
|
||
run: |
|
||
echo "🔍 检查Node.js环境..."
|
||
export PATH=$PATH:/usr/local/bin
|
||
node --version
|
||
npm --version
|
||
echo "Node.js路径: $(which node)"
|
||
echo "npm路径: $(which npm)"
|
||
|
||
- name: 安装pnpm
|
||
shell: bash
|
||
run: |
|
||
echo "📦 安装pnpm包管理器..."
|
||
export PATH=$PATH:/usr/local/bin
|
||
if command -v pnpm &> /dev/null; then
|
||
echo "✅ pnpm已安装: $(pnpm --version)"
|
||
else
|
||
echo "📥 安装pnpm..."
|
||
npm install -g pnpm
|
||
echo "✅ pnpm安装完成: $(pnpm --version)"
|
||
fi
|
||
|
||
- name: 检查缓存目录权限
|
||
shell: bash
|
||
run: |
|
||
echo "🔍 检查缓存目录权限和状态..."
|
||
echo "=== /opt/hostedtoolcache2 目录检查 ==="
|
||
ls -la /opt/hostedtoolcache2/ 2>/dev/null || echo "/opt/hostedtoolcache2 目录不存在或无法访问"
|
||
echo "=== /shared/cache 目录检查 ==="
|
||
ls -la /shared/cache/ 2>/dev/null || echo "/shared/cache 目录不存在或无法访问"
|
||
echo "=== /tmp 目录检查 ==="
|
||
ls -la /tmp/ | head -10
|
||
echo "=== 当前工作目录 ==="
|
||
pwd
|
||
ls -la
|
||
|
||
- name: 配置hostedtoolcache缓存目录
|
||
shell: bash
|
||
run: |
|
||
echo "🔧 配置 /opt/hostedtoolcache2 缓存目录..."
|
||
|
||
# 检查 /opt/hostedtoolcache2 目录
|
||
if [ ! -d "/opt/hostedtoolcache2" ]; then
|
||
echo "📁 /opt/hostedtoolcache2 目录不存在,尝试创建..."
|
||
if mkdir -p "/opt/hostedtoolcache2" 2>/dev/null; then
|
||
echo "✅ /opt/hostedtoolcache2 目录创建成功"
|
||
else
|
||
echo "❌ /opt/hostedtoolcache2 目录创建失败"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
# 检查目录权限
|
||
if [ -w "/opt/hostedtoolcache2" ]; then
|
||
echo "✅ /opt/hostedtoolcache2 目录可写"
|
||
# 设置权限
|
||
chmod 755 "/opt/hostedtoolcache2" 2>/dev/null || echo "⚠️ 无法设置权限"
|
||
|
||
# 创建测试文件
|
||
TEST_FILE="/opt/hostedtoolcache2/test_write_$(date +%s)"
|
||
if touch "$TEST_FILE" 2>/dev/null; then
|
||
echo "✅ /opt/hostedtoolcache2 写入测试成功"
|
||
rm -f "$TEST_FILE"
|
||
echo "CACHE_DIR=/opt/hostedtoolcache2" >> $GITHUB_ENV
|
||
else
|
||
echo "❌ /opt/hostedtoolcache2 写入测试失败"
|
||
exit 1
|
||
fi
|
||
else
|
||
echo "❌ /opt/hostedtoolcache2 目录不可写"
|
||
echo "🔧 尝试修改目录权限..."
|
||
chmod 755 "/opt/hostedtoolcache2" 2>/dev/null || echo "⚠️ 无法修改权限"
|
||
if [ -w "/opt/hostedtoolcache2" ]; then
|
||
echo "✅ 权限修改成功"
|
||
echo "CACHE_DIR=/opt/hostedtoolcache2" >> $GITHUB_ENV
|
||
else
|
||
echo "❌ 无法获得 /opt/hostedtoolcache2 写入权限"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
echo "📊 缓存目录配置: /opt/hostedtoolcache2"
|
||
echo "📁 目录内容:"
|
||
ls -la /opt/hostedtoolcache2/ 2>/dev/null || echo "目录为空或无法访问"
|
||
|
||
- name: 恢复依赖缓存
|
||
shell: bash
|
||
run: |
|
||
echo "📦 检查并恢复依赖缓存..."
|
||
|
||
# 生成缓存键
|
||
CACHE_KEY=$(md5sum pnpm-lock.yaml | cut -d' ' -f1)
|
||
echo "缓存键: $CACHE_KEY"
|
||
|
||
# 使用 /opt/hostedtoolcache2 目录
|
||
CACHE_FILE="/opt/hostedtoolcache2/node_modules_${CACHE_KEY}.tar.gz"
|
||
echo "📁 检查缓存文件: $CACHE_FILE"
|
||
|
||
if [ -f "$CACHE_FILE" ]; then
|
||
echo "✅ 找到缓存文件: $CACHE_FILE"
|
||
echo "📦 缓存文件大小: $(du -sh "$CACHE_FILE" | cut -f1)"
|
||
echo "📅 缓存文件修改时间: $(stat -c %y "$CACHE_FILE")"
|
||
echo "正在恢复缓存..."
|
||
|
||
if tar -xzf "$CACHE_FILE"; then
|
||
echo "✅ 缓存恢复成功"
|
||
echo "📦 恢复的 node_modules 大小:"
|
||
du -sh node_modules 2>/dev/null || echo "node_modules 目录不存在"
|
||
else
|
||
echo "❌ 缓存恢复失败"
|
||
echo "📥 将重新安装依赖"
|
||
fi
|
||
else
|
||
echo "📥 未找到缓存文件,将重新安装依赖"
|
||
fi
|
||
|
||
- name: 创建测试数据
|
||
shell: bash
|
||
run: |
|
||
echo "📦 创建测试数据(模拟node_modules)..."
|
||
export PATH=$PATH:/usr/local/bin
|
||
echo "开始时间: $(date)"
|
||
|
||
# 检查是否已有测试数据
|
||
if [ -d "node_modules" ]; then
|
||
echo "✅ 检测到已存在的测试数据,跳过创建"
|
||
else
|
||
echo "📥 创建模拟的node_modules目录..."
|
||
mkdir -p node_modules
|
||
|
||
# 创建一些模拟的包目录
|
||
PACKAGES=("react" "vue" "angular" "typescript" "webpack" "vite" "eslint" "prettier")
|
||
for pkg in "${PACKAGES[@]}"; do
|
||
mkdir -p "node_modules/$pkg"
|
||
echo "{\"name\":\"$pkg\",\"version\":\"1.0.0\",\"description\":\"Test package $pkg\"}" > "node_modules/$pkg/package.json"
|
||
echo "// Test file for $pkg" > "node_modules/$pkg/index.js"
|
||
echo "export default '$pkg';" > "node_modules/$pkg/index.ts"
|
||
done
|
||
|
||
# 创建一些模拟的配置文件
|
||
echo "{\"name\":\"epic-ui\",\"version\":\"1.0.0\"}" > "node_modules/package.json"
|
||
echo "test" > "node_modules/.pnpm-debug.log"
|
||
|
||
# 创建一些大文件来模拟真实的node_modules大小
|
||
echo "📦 创建大文件模拟真实依赖大小..."
|
||
dd if=/dev/zero of="node_modules/large_file_1.bin" bs=1M count=10 2>/dev/null || echo "创建大文件1完成"
|
||
dd if=/dev/zero of="node_modules/large_file_2.bin" bs=1M count=15 2>/dev/null || echo "创建大文件2完成"
|
||
dd if=/dev/zero of="node_modules/large_file_3.bin" bs=1M count=20 2>/dev/null || echo "创建大文件3完成"
|
||
|
||
echo "✅ 测试数据创建完成"
|
||
fi
|
||
|
||
echo "📦 node_modules 目录大小:"
|
||
du -sh node_modules 2>/dev/null || echo "node_modules 目录不存在"
|
||
echo "📁 node_modules 目录内容:"
|
||
ls -la node_modules/ | head -10
|
||
|
||
- name: 保存依赖缓存
|
||
shell: bash
|
||
run: |
|
||
echo "💾 保存依赖缓存..."
|
||
|
||
# 生成缓存键
|
||
CACHE_KEY=$(md5sum pnpm-lock.yaml | cut -d' ' -f1)
|
||
echo "缓存键: $CACHE_KEY"
|
||
|
||
# 检查node_modules是否存在
|
||
if [ ! -d "node_modules" ]; then
|
||
echo "❌ node_modules 目录不存在,跳过缓存保存"
|
||
exit 0
|
||
fi
|
||
|
||
# 保存到 /opt/hostedtoolcache2 目录
|
||
CACHE_FILE="/opt/hostedtoolcache2/node_modules_${CACHE_KEY}.tar.gz"
|
||
echo "📦 正在创建缓存文件: $CACHE_FILE"
|
||
|
||
if tar -czf "$CACHE_FILE" node_modules; then
|
||
echo "✅ 缓存已保存到: $CACHE_FILE"
|
||
echo "📦 缓存文件大小: $(du -sh "$CACHE_FILE" | cut -f1)"
|
||
echo "📅 缓存文件创建时间: $(stat -c %y "$CACHE_FILE")"
|
||
|
||
# 显示缓存目录内容
|
||
echo "🔍 /opt/hostedtoolcache2 目录内容:"
|
||
ls -la /opt/hostedtoolcache2/ | head -10
|
||
|
||
# 创建测试文件验证写入权限
|
||
echo "🧪 创建测试文件验证写入权限..."
|
||
TEST_FILE="/opt/hostedtoolcache2/test_file_$(date +%s).txt"
|
||
echo "测试时间: $(date)" > "$TEST_FILE"
|
||
if [ -f "$TEST_FILE" ]; then
|
||
echo "✅ 测试文件创建成功: $TEST_FILE"
|
||
echo "📄 测试文件内容: $(cat "$TEST_FILE")"
|
||
else
|
||
echo "❌ 测试文件创建失败"
|
||
fi
|
||
else
|
||
echo "❌ 缓存保存失败"
|
||
exit 1
|
||
fi
|
||
|
||
echo "⏰ 等待1分钟,让你检查缓存是否成功写入..."
|
||
echo "🕐 当前时间: $(date)"
|
||
echo "📊 缓存状态检查:"
|
||
|
||
# 添加详细的诊断信息
|
||
echo "🔍 系统诊断信息:"
|
||
echo "容器ID: $(cat /proc/self/cgroup | grep docker | head -1 | cut -d'/' -f3 || echo '无法获取容器ID')"
|
||
echo "当前用户: $(whoami)"
|
||
echo "用户ID: $(id)"
|
||
echo "工作目录: $(pwd)"
|
||
echo "挂载点信息:"
|
||
mount | grep hostedtoolcache || echo "未找到hostedtoolcache挂载点"
|
||
echo "磁盘使用情况:"
|
||
df -h /opt/hostedtoolcache2 2>/dev/null || echo "无法获取/opt/hostedtoolcache2磁盘信息"
|
||
echo "---"
|
||
|
||
# 每5秒检查一次缓存状态,持续60秒
|
||
for i in {1..1}; do
|
||
echo "=== 第 $i 次检查 (第 $((i*5)) 秒) ==="
|
||
echo "时间: $(date)"
|
||
|
||
echo "📁 /opt/hostedtoolcache2 目录详细信息:"
|
||
echo "目录权限: $(ls -ld /opt/hostedtoolcache2 2>/dev/null || echo '无法获取权限')"
|
||
echo "目录所有者: $(stat -c '%U:%G' /opt/hostedtoolcache2 2>/dev/null || echo '无法获取所有者')"
|
||
echo "目录内容:"
|
||
ls -la /opt/hostedtoolcache2/ 2>/dev/null | head -10 || echo "无法访问 /opt/hostedtoolcache2"
|
||
|
||
if [ -f "$CACHE_FILE" ]; then
|
||
echo "✅ 缓存文件存在: $CACHE_FILE"
|
||
echo "📦 文件大小: $(du -sh "$CACHE_FILE" 2>/dev/null | cut -f1 || echo '无法获取大小')"
|
||
echo "📅 修改时间: $(stat -c %y "$CACHE_FILE" 2>/dev/null || echo '无法获取时间')"
|
||
echo "文件权限: $(ls -l "$CACHE_FILE" 2>/dev/null || echo '无法获取权限')"
|
||
echo "文件所有者: $(stat -c '%U:%G' "$CACHE_FILE" 2>/dev/null || echo '无法获取所有者')"
|
||
else
|
||
echo "❌ 缓存文件不存在: $CACHE_FILE"
|
||
fi
|
||
echo "---"
|
||
|
||
if [ $i -lt 12 ]; then
|
||
echo "⏳ 等待5秒..."
|
||
sleep 5
|
||
fi
|
||
done
|
||
|
||
echo "✅ 1分钟等待完成,缓存检查结束"
|
||
echo "🕐 结束时间: $(date)"
|
||
|
||
- name: 验证测试数据
|
||
shell: bash
|
||
run: |
|
||
echo "🔍 验证测试数据..."
|
||
export PATH=$PATH:/usr/local/bin
|
||
|
||
if [ -d "node_modules" ]; then
|
||
echo "✅ node_modules 目录存在"
|
||
echo "📦 目录大小: $(du -sh node_modules | cut -f1)"
|
||
echo "📁 包含的包:"
|
||
ls -la node_modules/ | grep "^d" | head -5
|
||
echo "📄 模拟包数量: $(find node_modules -maxdepth 1 -type d | wc -l)"
|
||
else
|
||
echo "❌ node_modules 目录不存在"
|
||
fi
|
||
|
||
echo "✅ 测试数据验证完成"
|
||
|
||
- name: 模拟构建项目
|
||
shell: bash
|
||
run: |
|
||
echo "🔨 模拟构建 Epic UI 前端项目..."
|
||
export PATH=$PATH:/usr/local/bin
|
||
|
||
# 创建模拟的构建产物
|
||
mkdir -p dist
|
||
echo "<!DOCTYPE html><html><head><title>Epic UI</title></head><body><h1>Epic UI - 构建成功</h1></body></html>" > dist/index.html
|
||
echo "body { font-family: Arial, sans-serif; }" > dist/style.css
|
||
echo "console.log('Epic UI loaded');" > dist/app.js
|
||
|
||
# 创建一些模拟的构建文件
|
||
mkdir -p dist/assets
|
||
echo "// 模拟的构建产物" > dist/assets/bundle.js
|
||
echo "/* 模拟的样式文件 */" > dist/assets/bundle.css
|
||
|
||
echo "✅ 模拟构建完成"
|
||
|
||
- name: 检查构建产物
|
||
shell: bash
|
||
run: |
|
||
echo "📦 构建产物信息:"
|
||
if [ -d "dist" ]; then
|
||
echo "✅ 找到dist目录"
|
||
ls -la dist/
|
||
echo "dist目录大小: $(du -sh dist | cut -f1)"
|
||
else
|
||
echo "❌ 未找到dist目录"
|
||
exit 1
|
||
fi
|
||
|
||
- name: 显示项目信息
|
||
shell: bash
|
||
run: |
|
||
echo "📋 项目信息:"
|
||
export PATH=$PATH:/usr/local/bin
|
||
echo "Node.js 版本: $(node --version)"
|
||
echo "npm 版本: $(npm --version)"
|
||
echo "pnpm 版本: $(pnpm --version)"
|
||
echo "构建时间: $(date)"
|
||
echo "分支: ${{ gitea.ref }}"
|
||
echo "提交: ${{ gitea.sha }}"
|
||
|
||
- name: 准备部署
|
||
shell: bash
|
||
run: |
|
||
echo "🚀 准备部署..."
|
||
echo "运行器操作系统: ${{ runner.os }}"
|
||
echo "工作目录: ${{ gitea.workspace }}"
|
||
echo "任务状态: ${{ job.status }}"
|
||
mkdir -p deploy
|
||
cp -r dist/* deploy/
|
||
echo "📦 部署包已准备完成"
|
||
|
||
- name: 验证 /opt/hostedtoolcache2 挂载情况(开始)
|
||
shell: bash
|
||
run: |
|
||
echo "==== [挂载点检查-开始] ===="
|
||
echo "当前时间: $(date)"
|
||
echo "主机名: $(hostname)"
|
||
echo "当前用户: $(whoami)"
|
||
echo "工作目录: $(pwd)"
|
||
echo "[mount | grep hostedtoolcache] 输出:"
|
||
mount | grep hostedtoolcache || echo "未找到hostedtoolcache挂载点"
|
||
echo "[df -h /opt/hostedtoolcache2] 输出:"
|
||
df -h /opt/hostedtoolcache2 2>/dev/null || echo "无法获取/opt/hostedtoolcache2磁盘信息"
|
||
echo "[ls -ld /opt/hostedtoolcache2] 输出:"
|
||
ls -ld /opt/hostedtoolcache2 2>/dev/null || echo "无法获取/opt/hostedtoolcache2权限"
|
||
echo "[stat /opt/hostedtoolcache2] 输出:"
|
||
stat /opt/hostedtoolcache2 2>/dev/null || echo "无法获取/opt/hostedtoolcache2 stat"
|
||
echo "[ls -lai /opt/hostedtoolcache2] 输出:"
|
||
ls -lai /opt/hostedtoolcache2 2>/dev/null || echo "无法获取/opt/hostedtoolcache2内容"
|
||
echo "==== [挂载点检查-开始] ===="
|
||
|
||
- name: 验证 /opt/hostedtoolcache2 挂载情况(保存缓存后)
|
||
shell: bash
|
||
run: |
|
||
echo "==== [挂载点检查-保存缓存后] ===="
|
||
echo "当前时间: $(date)"
|
||
echo "主机名: $(hostname)"
|
||
echo "当前用户: $(whoami)"
|
||
echo "工作目录: $(pwd)"
|
||
echo "[mount | grep hostedtoolcache] 输出:"
|
||
mount | grep hostedtoolcache || echo "未找到hostedtoolcache挂载点"
|
||
echo "[df -h /opt/hostedtoolcache2] 输出:"
|
||
df -h /opt/hostedtoolcache2 2>/dev/null || echo "无法获取/opt/hostedtoolcache2磁盘信息"
|
||
echo "[ls -ld /opt/hostedtoolcache2] 输出:"
|
||
ls -ld /opt/hostedtoolcache2 2>/dev/null || echo "无法获取/opt/hostedtoolcache2权限"
|
||
echo "[stat /opt/hostedtoolcache2] 输出:"
|
||
stat /opt/hostedtoolcache2 2>/dev/null || echo "无法获取/opt/hostedtoolcache2 stat"
|
||
echo "[ls -lai /opt/hostedtoolcache2] 输出:"
|
||
ls -lai /opt/hostedtoolcache2 2>/dev/null || echo "无法获取/opt/hostedtoolcache2内容"
|
||
echo "==== [挂载点检查-保存缓存后] ===="
|
||
|
||
# - name: 复制构建产物到目标目录
|
||
# shell: bash
|
||
# run: |
|
||
# echo "📤 复制构建产物到 /opt/dockeropt/epic-ui ..."
|
||
# mkdir -p /opt/dockeropt/epic-ui
|
||
# cp -r dist/* /opt/dockeropt/epic-ui/
|
||
# echo "✅ 构建产物已复制到 /opt/dockeropt/epic-ui"
|
||
# ls -la /opt/dockeropt/epic-ui/
|
||
|
||
# - name: 重启 Docker 容器
|
||
# shell: bash
|
||
# run: |
|
||
# echo "♻️ 重启 epic-ui 容器..."
|
||
# if ! command -v docker &> /dev/null; then
|
||
# if command -v apt-get &> /dev/null; then
|
||
# apt-get update && apt-get install -y docker.io
|
||
# elif command -v apk &> /dev/null; then
|
||
# echo "http://mirrors.aliyun.com/alpine/v3.20/main" > /etc/apk/repositories
|
||
# echo "http://mirrors.aliyun.com/alpine/v3.20/community" >> /etc/apk/repositories
|
||
# apk update
|
||
# apk add --no-cache docker-cli
|
||
# elif command -v yum &> /dev/null; then
|
||
# yum install -y docker
|
||
# fi
|
||
# fi
|
||
# docker ps -a
|
||
# docker restart epic-ui
|
||
# echo "✅ epic-ui 容器已重启" |