Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bd17d23c30 | |||
| 2357e6d45a | |||
| e85c9f6086 |
177
.gitea/workflows/reusable-prepare-workspace.yml
Normal file
177
.gitea/workflows/reusable-prepare-workspace.yml
Normal file
@@ -0,0 +1,177 @@
|
||||
name: Reusable Prepare Workspace
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
repo_url:
|
||||
description: "Git 仓库地址"
|
||||
required: true
|
||||
type: string
|
||||
git_sha:
|
||||
description: "目标提交 SHA"
|
||||
required: true
|
||||
type: string
|
||||
node_major:
|
||||
description: "Node 主版本(例如 22)"
|
||||
required: false
|
||||
type: string
|
||||
default: "22"
|
||||
node_version:
|
||||
description: "Node 精确版本(例如 22.12.0,优先于 node_major)"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
yarn_version:
|
||||
description: "Yarn 版本(例如 stable 或 1.22.22)"
|
||||
required: false
|
||||
type: string
|
||||
default: "stable"
|
||||
nodejs_mirror:
|
||||
description: "Node 二进制镜像地址"
|
||||
required: false
|
||||
type: string
|
||||
default: "https://npmmirror.com/mirrors/node"
|
||||
npm_registry:
|
||||
description: "NPM 镜像地址"
|
||||
required: false
|
||||
type: string
|
||||
default: "https://registry.npmmirror.com"
|
||||
node_install_root:
|
||||
description: "Node 安装缓存目录"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
run_commands:
|
||||
description: "准备完成后执行的额外命令(多行 shell)"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
secrets:
|
||||
SSH_PRIVATE_KEY:
|
||||
required: false
|
||||
REGISTRY_USERNAME:
|
||||
required: false
|
||||
REGISTRY_PASSWORD:
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
REPO_URL: ${{ inputs.repo_url }}
|
||||
GIT_SHA: ${{ inputs.git_sha }}
|
||||
NODE_MAJOR: ${{ inputs.node_major }}
|
||||
NODE_VERSION: ${{ inputs.node_version }}
|
||||
YARN_VERSION: ${{ inputs.yarn_version }}
|
||||
NODEJS_MIRROR: ${{ inputs.nodejs_mirror }}
|
||||
NPM_REGISTRY: ${{ inputs.npm_registry }}
|
||||
NODE_INSTALL_ROOT: ${{ inputs.node_install_root }}
|
||||
RUN_COMMANDS: ${{ inputs.run_commands }}
|
||||
steps:
|
||||
- name: Prepare workspace (checkout + node/yarn)
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
resolve_node_version() {
|
||||
if [ -n "${NODE_VERSION}" ]; then
|
||||
echo "${NODE_VERSION#v}"
|
||||
return
|
||||
fi
|
||||
|
||||
local resolved
|
||||
resolved="$(curl -fsSL "${NODEJS_MIRROR}/index.tab" | awk -F'\t' -v major="${NODE_MAJOR}" '
|
||||
NR > 1 && $1 ~ ("^v" major "\\.") { print substr($1, 2); exit }
|
||||
')"
|
||||
if [ -z "${resolved}" ]; then
|
||||
echo "failed to resolve latest Node ${NODE_MAJOR}.x from ${NODEJS_MIRROR}"
|
||||
exit 1
|
||||
fi
|
||||
echo "${resolved}"
|
||||
}
|
||||
|
||||
if [ -z "${NODE_INSTALL_ROOT}" ]; then
|
||||
NODE_INSTALL_ROOT="${HOME}/.local/node-ci"
|
||||
fi
|
||||
|
||||
detect_arch() {
|
||||
case "$(uname -m)" in
|
||||
x86_64) echo "x64" ;;
|
||||
aarch64 | arm64) echo "arm64" ;;
|
||||
*)
|
||||
echo "unsupported architecture: $(uname -m)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
install_node() {
|
||||
local target_version="$1"
|
||||
local arch name url tarball install_dir bindir
|
||||
arch="$(detect_arch)"
|
||||
name="node-v${target_version}-linux-${arch}"
|
||||
url="${NODEJS_MIRROR}/v${target_version}/${name}.tar.xz"
|
||||
tarball="/tmp/${name}.tar.xz"
|
||||
install_dir="${NODE_INSTALL_ROOT}/${name}"
|
||||
bindir="${install_dir}/bin"
|
||||
|
||||
if [ ! -x "${bindir}/node" ]; then
|
||||
mkdir -p "${NODE_INSTALL_ROOT}"
|
||||
curl -fsSL "${url}" -o "${tarball}"
|
||||
tar -xJf "${tarball}" -C "${NODE_INSTALL_ROOT}"
|
||||
rm -f "${tarball}"
|
||||
fi
|
||||
|
||||
export PATH="${bindir}:${PATH}"
|
||||
}
|
||||
|
||||
ensure_node() {
|
||||
local target_version node_version
|
||||
target_version="$(resolve_node_version)"
|
||||
|
||||
if command -v node >/dev/null 2>&1; then
|
||||
node_version="$(node -v | sed 's/^v//')"
|
||||
if [ "${node_version}" = "${target_version}" ]; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
install_node "${target_version}"
|
||||
|
||||
node_version="$(node -v | sed 's/^v//')"
|
||||
if [ "${node_version}" != "${target_version}" ]; then
|
||||
echo "failed to switch to Node ${target_version}, current=${node_version}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
if [ ! -d .git ]; then
|
||||
git clone "${REPO_URL}" .
|
||||
fi
|
||||
|
||||
git fetch --all --tags --prune
|
||||
git checkout -f "${GIT_SHA}"
|
||||
|
||||
ensure_node
|
||||
|
||||
npm config set registry "${NPM_REGISTRY}" >/dev/null 2>&1 || true
|
||||
|
||||
if ! command -v yarn >/dev/null 2>&1; then
|
||||
corepack enable
|
||||
export COREPACK_NPM_REGISTRY="${NPM_REGISTRY}"
|
||||
corepack prepare "yarn@${YARN_VERSION}" --activate
|
||||
fi
|
||||
|
||||
echo "Node: $(node -v)"
|
||||
echo "Yarn: $(yarn -v)"
|
||||
|
||||
- name: Run extra commands
|
||||
if: ${{ inputs.run_commands != '' }}
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
printf '%s\n' "${RUN_COMMANDS}" > /tmp/reusable-prepare-run.sh
|
||||
chmod +x /tmp/reusable-prepare-run.sh
|
||||
/tmp/reusable-prepare-run.sh
|
||||
@@ -1,19 +1,25 @@
|
||||
# 公共可复用工作流(workflow_call)。调用方传入:node_version、yarn_version、project_dir。
|
||||
# 部署主机/用户/父路径、SSH 私钥从「调用方」仓库的 vars / secrets 读取(同仓 uses: ./... 时即本仓库配置)。
|
||||
# 公共可复用工作流(workflow_call):Node SPA 构建后通过 SSH + tar 部署到 Nginx。
|
||||
#
|
||||
# 调用方 ci.yml 示例:
|
||||
# 必选 inputs:node_version、yarn_version、project_dir
|
||||
# 可选 inputs:build_script(默认 yarn build)、build_output_dir(默认 dist;若以 / 开头会去掉首字符,如 /dist → dist)
|
||||
#
|
||||
# 部署目标与认证:DEPLOY_PATH、DEPLOY_HOST、DEPLOY_USER(仓库 Variables)、SSH_PRIVATE_KEY(Secrets)
|
||||
# 由「调用方」仓库提供;同仓库 uses: ./.gitea/workflows/... 时即读取调用方本仓配置。
|
||||
#
|
||||
# 调用方工作流示例:
|
||||
# jobs:
|
||||
# build:
|
||||
# uses: ./.gitea/workflows/node-spa-tar-deploy.reusable.yml@main
|
||||
# deploy:
|
||||
# uses: ./.gitea/workflows/web-spa-deploy.yml@main
|
||||
# with:
|
||||
# node_version: "22.14.0"
|
||||
# yarn_version: "1.22.22"
|
||||
# project_dir: "chat-one-web"
|
||||
# build_script: "yarn build"
|
||||
# build_output_dir: "dist"
|
||||
# secrets: inherit
|
||||
#
|
||||
# 调用方「工作流 → 变量」:DEPLOY_PATH、DEPLOY_HOST、DEPLOY_USER
|
||||
# 调用方「工作流 → 密钥」:SSH_PRIVATE_KEY
|
||||
# 远端发布目录 = ${DEPLOY_PATH}/${project_dir}
|
||||
# 远端站点根目录:${DEPLOY_PATH}/${project_dir}
|
||||
# 仅当事件为 push 且分支为 main 或 master 时执行部署步骤(Install OpenSSH client、Deploy to Nginx)。
|
||||
|
||||
name: Reusable Node SPA + tar deploy
|
||||
|
||||
@@ -32,6 +38,16 @@ on:
|
||||
description: "站点子目录(相对 DEPLOY_PATH,如 chat-one-web)"
|
||||
required: true
|
||||
type: string
|
||||
build_script:
|
||||
description: "构建命令(单行 shell,如 yarn build)"
|
||||
required: false
|
||||
type: string
|
||||
default: "yarn build"
|
||||
build_output_dir:
|
||||
description: "构建产物目录(相对仓库根;若以 / 开头会去掉首字符 /,如 /dist → dist)"
|
||||
required: false
|
||||
type: string
|
||||
default: "dist"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -111,14 +127,8 @@ jobs:
|
||||
- name: Install
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Prettier check
|
||||
run: yarn format:check
|
||||
|
||||
- name: ESLint
|
||||
run: yarn lint
|
||||
|
||||
- name: Build
|
||||
run: yarn build
|
||||
run: ${{ inputs.build_script }}
|
||||
|
||||
- name: Install OpenSSH client
|
||||
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master')
|
||||
@@ -141,12 +151,18 @@ jobs:
|
||||
env:
|
||||
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
|
||||
PROJECT_DIR: ${{ inputs.project_dir }}
|
||||
BUILD_OUT: ${{ inputs.build_output_dir }}
|
||||
run: |
|
||||
set -e
|
||||
: "${DEPLOY_PATH:?DEPLOY_PATH (vars) is not set}"
|
||||
: "${DEPLOY_HOST:?DEPLOY_HOST (vars) is not set}"
|
||||
: "${DEPLOY_USER:?DEPLOY_USER (vars) is not set}"
|
||||
: "${PROJECT_DIR:?project_dir is not set}"
|
||||
OUT="${BUILD_OUT#/}"
|
||||
if [ ! -d "$OUT" ]; then
|
||||
echo "构建产物目录不存在: $OUT(请检查 build_output_dir 与构建脚本)" >&2
|
||||
exit 1
|
||||
fi
|
||||
REMOTE_ROOT="${DEPLOY_PATH}/${PROJECT_DIR}"
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
@@ -155,4 +171,4 @@ jobs:
|
||||
ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
|
||||
SSH=(ssh -i ~/.ssh/id_deploy -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes)
|
||||
"${SSH[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" "mkdir -p '${REMOTE_ROOT}' && find '${REMOTE_ROOT}' -mindepth 1 -delete"
|
||||
tar czf - -C dist . | "${SSH[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" "tar xzf - -C '${REMOTE_ROOT}'"
|
||||
tar czf - -C "$OUT" . | "${SSH[@]}" "${DEPLOY_USER}@${DEPLOY_HOST}" "tar xzf - -C '${REMOTE_ROOT}'"
|
||||
|
||||
Reference in New Issue
Block a user