Appearance
GitHub Actions
工作原理
GitHub Actions 是 GitHub 内置的 CI/CD 自动化平台。每次推送代码后,不需要手动连接服务器,GitHub 会自动帮你完成构建、测试、部署等一系列操作。
完整执行过程:
- 开发者推送代码到 GitHub 仓库
- GitHub 检测到触发事件(如 push 到 main 分支)
- 分配一台托管虚拟机(Runner),克隆仓库代码
- Runner 按工作流文件依次执行 Job 和 Step
- 执行结果记录到仓库的 Actions 选项卡,可配置失败通知
| 概念 | 说明 |
|---|---|
| Workflow | 工作流,定义在 .github/workflows/*.yml 中,由事件触发 |
| Job | 工作流中的任务,默认并行运行,可用 needs 设置依赖顺序 |
| Step | Job 中的单个步骤,按顺序执行,可运行命令或调用 Action |
| Action | 可复用的操作单元,来自 GitHub Marketplace 或自定义 |
| Runner | 执行 Job 的虚拟机,GitHub 提供 ubuntu/windows/macos 三类 |
| Secret | 仓库级加密变量,在工作流中用 ${{ secrets.NAME }} 引用 |
每个仓库最多 20 个工作流同时运行,超出部分会被 GitHub 自动暂停。
GitHub 仓库配置
GitHub Actions 在公开仓库默认开启,私有仓库也默认可用(Free 计划每月有 2000 分钟免费额度)。使用前需确认以下两项配置。
开启 Actions 权限
进入仓库 → Settings → Actions → General,确认 Actions permissions 选择的是 Allow all actions and reusable workflows(默认值)。如果是组织仓库且 Actions 被禁用,需要由组织管理员在组织设置中开启。
工作流权限(GITHUB_TOKEN)
每次工作流运行时,GitHub 会自动注入一个临时 Token(${{ secrets.GITHUB_TOKEN }}),用于读写仓库内容(如发布 Release、推送到 gh-pages 分支)。默认权限为只读,如需写入,进入仓库 → Settings → Actions → General → Workflow permissions,选择 Read and write permissions。
自己配置的 Secrets(如
SSH_PRIVATE_KEY)与GITHUB_TOKEN是两套不同的机制:前者是手动添加的敏感变量,后者是 GitHub 自动颁发的临时凭证。
查看运行结果
工作流触发后,在仓库顶部点击 Actions 选项卡,可以看到所有历史运行记录。点击某次运行,展开对应 Job 和 Step,可以实时查看日志输出,失败时会高亮显示具体报错行。
工作流配置
文件位置与命名
工作流文件存放在仓库的 .github/workflows/ 目录下,使用 .yml 格式,文件名建议与用途对应:
bash
.github/
workflows/
deploy.yml # 部署到服务器
ci.yml # 持续集成(lint / 测试)每个工作流文件独立触发、独立执行,互不影响。
触发事件(on)
on 字段定义什么情况下触发这个工作流:
yaml
on:
# 推送到指定分支时触发(最常用)
push:
branches:
- main
- 'release/*' # 支持通配符
# 向指定分支发起 PR 时触发
pull_request:
branches:
- main
# 定时触发(UTC 时间)
schedule:
- cron: '0 2 * * *' # 每天北京时间 10:00(UTC+8 偏移 -8)
# 允许在 GitHub 界面手动触发
workflow_dispatch:常用事件一览:
| 事件 | 触发时机 |
|---|---|
push | 推送提交到指定分支/标签时 |
pull_request | PR 被创建、更新、关闭时 |
schedule | 按 cron 定时触发(最小间隔 5 分钟) |
workflow_dispatch | 手动触发,支持在 GitHub UI 传入参数 |
release | 发布新版本、编辑或删除 Release 时 |
定时任务(cron)
cron 字段格式为 分 时 日 月 周,以 UTC 时间为准:
yaml
on:
schedule:
- cron: '0 2 * * *' # 每天 UTC 02:00(北京时间 10:00)
- cron: '0 2 * * 1-5' # 周一到周五 UTC 02:00
- cron: '*/30 * * * *' # 每 30 分钟(最小粒度为 5 分钟)| 符号 | 含义 |
|---|---|
* | 任意值 |
, | 多个值,如 1,3,5 |
- | 范围,如 1-5 |
/ | 间隔,如 */15 表示每 15 分钟 |
GitHub Actions 定时任务在高峰期可能有延迟,不适合对精确时间要求极高的场景。
Job 配置
一个工作流可以包含多个 Job。Job 默认并行,用 needs 可以设置执行顺序:
yaml
jobs:
build:
runs-on: ubuntu-latest # 在 Ubuntu 虚拟机上运行
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
deploy:
runs-on: ubuntu-latest
needs: build # 等 build 完成后才执行
if: github.ref == 'refs/heads/main' # 只有 main 分支才部署
environment: production # 关联 GitHub 环境,可配置审批保护
timeout-minutes: 30 # 超时自动取消,防止卡死
steps:
- run: echo "开始部署"runs-on 常用值:ubuntu-latest、ubuntu-22.04、windows-latest、macos-latest。完整列表见 runner-images。
矩阵策略(同时在多个环境下测试):
yaml
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node: [18, 20, 22] # 并行创建 3 个 Job 实例,分别测试三个版本
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm testStep 配置
每个 Step 可以执行命令(run)或调用 Action(uses):
yaml
steps:
- name: 检出代码
uses: actions/checkout@v4 # 把仓库代码下载到 Runner 上,几乎每个工作流都需要
- name: 安装 Node.js
uses: actions/setup-node@v4
with:
node-version: '20' # with 为 Action 提供输入参数
- name: 安装依赖
run: npm ci # run 执行 Shell 命令
- name: 多行命令
run: |
echo "开始构建..."
npm run build
echo "构建完成"
- name: 设置 Step 级环境变量
run: echo "打印 $MY_VAR"
env:
MY_VAR: hello # 仅本 Step 生效,不污染全局在 GitHub Marketplace 可搜索社区提供的各类 Action,如 actions/setup-node、appleboy/ssh-action 等。
内置变量可在工作流任意位置引用,格式为 ${{ github.XXXX }}:
| 变量 | 说明 |
|---|---|
${{ github.actor }} | 触发工作流的用户名 |
${{ github.repository }} | 仓库全名(owner/repo) |
${{ github.ref }} | 触发的分支(如 refs/heads/main) |
${{ github.sha }} | 当前提交的 SHA 值 |
${{ github.event_name }} | 触发事件名称(如 push、pull_request) |
Secrets 配置
Secrets 是仓库级的加密变量,用于存储密码、私钥、Token 等敏感信息。值写入后无法再次查看,只能覆盖或删除。工作流中通过 ${{ secrets.变量名 }} 引用。
添加步骤:
- 进入仓库 → Settings → Secrets and variables → Actions
- 点击 New repository secret
- 填写变量名(如
SSH_PRIVATE_KEY)和值,保存
常用 Secrets:
| Secret 名称 | 用途 |
|---|---|
SSH_HOST | 服务器 IP 地址 |
SSH_USER | 服务器登录用户名 |
SSH_PORT | SSH 端口(默认 22) |
SSH_PRIVATE_KEY | SSH 私钥(用于免密登录服务器) |
DEPLOY_PATH | 服务器上的部署目录路径 |
WECHAT_WEBHOOK_URL | 企业微信机器人 Webhook 地址 |
不要把密码、Token 直接写在
.yml文件里提交到仓库,一律通过 Secrets 传入。
SSH 免密登录
GitHub Actions 通过 SSH 私钥连接服务器执行远程命令或上传文件,需要提前在服务器上配置对应公钥。整体思路:把公钥存在服务器,把私钥存在 GitHub Secrets,GitHub 用私钥向服务器证明身份。
1. 在服务器上生成密钥对:
bash
ssh-keygen -t ed25519 -C "github-actions-deploy"
# 路径直接回车使用默认(~/.ssh/id_ed25519)
# 密码留空,直接两次回车生成两个文件:~/.ssh/id_ed25519(私钥)和 ~/.ssh/id_ed25519.pub(公钥)。
也可以用
rsa -b 4096,ed25519 更短更安全,推荐优先使用。
2. 将公钥追加到服务器授权列表:
bash
cat ~/.ssh/id_ed25519.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys # 权限必须为 600,SSH 才会读取此文件
chmod 700 ~/.ssh # .ssh 目录权限必须为 7003. 将私钥添加到 GitHub Secrets:
在服务器上执行以下命令,查看私钥内容:
bash
cat ~/.ssh/id_ed25519输出类似:
text
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAA...(中间内容省略)
-----END OPENSSH PRIVATE KEY-----全选复制(必须包含首尾的 BEGIN / END 行),然后:
- 进入仓库 → Settings → Secrets and variables → Actions
- 点击 New repository secret
- 名称填
SSH_PRIVATE_KEY,值粘贴刚才复制的全部内容,保存
私钥只存在服务器和 GitHub Secrets 中,不要复制到任何其他地方,也不要提交到代码仓库。
4. 验证 SSH 连接(可在本地测试):
bash
ssh -i ~/.ssh/id_ed25519 user@your-server-ip
# 能无密码进入服务器说明配置正确最佳实践
Vue / VitePress 静态站点部署(构建 + 上传服务器)
Vue、VitePress、React 等前端项目本质是静态文件:npm run build 后生成 dist/ 目录,把这个目录上传到服务器,Nginx 指向它就完成了部署。相比后端服务,静态站点不需要 Docker,构建在 GitHub Runner 上完成,直接传文件即可。
前提准备:
- 服务器已安装 Nginx 和 rsync(rsync 用于文件同步,缺少则部署报错)
- 部署目录已创建(如
/var/www/my-site) - 已完成 SSH 免密登录配置(私钥存在 Secret
SSH_PRIVATE_KEY) - 已添加 Secrets:
SSH_HOST、SSH_USER、SSH_PORT、DEPLOY_PATH
安装 rsync:
bash
sudo apt install rsync # Ubuntu / Debian
sudo yum install rsync # CentOS / RHEL工作流文件 .github/workflows/deploy.yml:
yaml
name: Build and Deploy
on:
push:
branches:
- main # 推送到 main 分支时自动触发
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 检出代码
uses: actions/checkout@v4
with:
fetch-depth: 0 # 默认只拉最新 1 条提交;0 表示拉取完整历史,VitePress lastUpdated 功能需要
- name: 安装 pnpm
uses: pnpm/action-setup@v4
with:
version: latest # npm 项目删除此 step,后面改用 npm ci
- name: 安装 Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm' # 缓存 pnpm store 加快依赖安装;npm 项目改为 'npm'
- name: 安装依赖
run: pnpm install # npm 项目改为:npm ci
- name: 构建
run: pnpm docs:build # VitePress 项目;普通 Vue 项目改为:pnpm build
- name: 上传到服务器
uses: appleboy/scp-action@master # 通过 SCP 传输文件到服务器
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
source: '.vitepress/dist/*' # VitePress 产物;Vue 项目改为:dist/*
target: ${{ secrets.DEPLOY_PATH }}
strip_components: 2 # 去掉 source 路径中的前两层目录(.vitepress/dist),文件直接落在 target 下scp-action 的上传行为:
scp-action 是全量覆盖,每次运行都把 source 匹配的所有文件重新传一遍,不做差量比对。对于静态站点(几十 MB 以内)这没有问题;文件量很大时改用 rsync 做增量同步(见下方说明)。
strip_components 的含义:
strip_components 控制上传时剥去 source 路径前缀的目录层数,等价于 tar --strip-components。
以 VitePress 为例,source: '.vitepress/dist/*',Runner 上的目录结构是:
text
.vitepress/
└── dist/ ← 第 1 层
├── index.html ← 文件
└── assets/ ← 子目录
└── app.jssource 匹配的文件相对路径是 .vitepress/dist/index.html,其中包含两层目录前缀(.vitepress 和 dist)。strip_components 就是告诉 scp 上传时把这个前缀的前 N 层去掉:
strip_components 值 | 服务器落地路径(DEPLOY_PATH = /var/www/my-site) |
|---|---|
| 不设置(默认 0) | /var/www/my-site/.vitepress/dist/index.html |
1 | /var/www/my-site/dist/index.html |
2 | /var/www/my-site/index.html ✓ 推荐 |
普通 Vue 项目产物在 dist/,source: 'dist/*' 只有 1 层前缀,设 strip_components: 1 即可。
指定目标目录:
target 直接写服务器上的绝对路径,strip_components 之后的文件直接落在这个目录下:
yaml
target: /var/www/my-site # 文件落在 /var/www/my-site/ 下
target: /var/www/my-site/static # 文件落在 /var/www/my-site/static/ 下
target目录不存在时scp-action会自动创建,但前提是登录用户对父目录有写入权限。
文件量较大时改用 rsync 增量同步:
rsync 只传本次构建中发生变化的文件(按文件大小和修改时间比对),比 scp 全量覆盖快很多。
使用前必须先在服务器上安装 rsync,否则部署时报
rsync: command not found错误。
bash
sudo apt install rsync # Ubuntu / Debian
sudo yum install rsync # CentOS / RHEL工作流替换上传步骤:
yaml
- name: rsync 增量同步到服务器
uses: burnett01/rsync-deployments@7.0.1
with:
switches: >-
-avz
--delete
--exclude='.git'
path: .vitepress/dist/ # VitePress 产物目录(末尾加 / 表示只传目录内容)
remote_path: ${{ secrets.DEPLOY_PATH }}
remote_host: ${{ secrets.SSH_HOST }}
remote_user: ${{ secrets.SSH_USER }}
remote_key: ${{ secrets.SSH_PRIVATE_KEY }}
remote_port: ${{ secrets.SSH_PORT }}rsync 常用参数说明:
| 参数 | 含义 |
|---|---|
-a | 归档模式,保留权限、时间戳、软链接等 |
-v | 输出传输详情,便于调试 |
-z | 传输时压缩,适合带宽较小的云服务器 |
--delete | 删除服务器上本次源目录中已不存在的文件 |
rsync方案不需要strip_components,因为path末尾加/后,rsync 直接传目录内容到remote_path,不会带目录层级。
服务器 Nginx 配置(Vue SPA / VitePress 通用写法,新建或修改 /etc/nginx/sites-available/my-site):
nginx
server {
listen 80;
server_name your-domain.com; # 替换为实际域名或 IP
root /var/www/my-site; # 对应 DEPLOY_PATH
index index.html;
location / {
try_files $uri $uri/ /index.html; # SPA 路由刷新时不返回 404,交由前端路由处理
}
}启用配置并重载 Nginx:
bash
sudo ln -s /etc/nginx/sites-available/my-site /etc/nginx/sites-enabled/
sudo nginx -t # 检查配置语法
sudo nginx -s reload # 重载配置第一次部署前,确保服务器目录存在且有写入权限:
bash
mkdir -p /var/www/my-site
chown -R $USER /var/www/my-siteNestJS 服务部署(SSH 远程执行 + Docker Compose + 企业微信通知)
这是简化方案,构建发生在服务器上,适合个人项目快速上手。生产团队推荐使用下一节的「CI 构建镜像 + GHCR」方案。
NestJS 等后端服务用 Docker Compose 运行,代码更新后需要在服务器上重新拉取代码、重建镜像并启动容器。与前端不同,后端的构建发生在服务器上(docker compose up --build),所以 GitHub Actions 这边只需 SSH 进去执行命令即可。
通知逻辑分两层:
- SSH 脚本内部捕获
docker compose的退出码,部署成功/失败分别通知 if: failure()捕获 SSH 连接失败、Action 本身报错,确保任何阶段出问题都能收到通知
前提准备:
- 服务器已克隆项目代码到
/home/project/nest-service,根目录有docker-compose.yml - 已完成 SSH 免密登录配置
- 已添加 Secrets:
SSH_HOST、SSH_USER、SSH_PORT、SSH_PRIVATE_KEY、WECHAT_WEBHOOK_URL
工作流文件 .github/workflows/deploy.yml:
yaml
name: Deploy NestJS Service
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: SSH 连接并执行部署
uses: appleboy/ssh-action@master # 免密 SSH 远程执行命令
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
script: |
cd /home/project/nest-service
git checkout -- . # 清除本地未提交改动,避免 pull 冲突
git pull origin main
docker compose up -d --build # 重建镜像并后台启动,旧容器自动替换
# 定义通知函数;$1=字体颜色 $2=结果文字
# 变量通过下方 env 注入,script 内不能直接用 ${{ secrets.* }}
notify() {
curl -s -X POST -H 'Content-Type: application/json' \
-d "{
\"msgtype\": \"markdown\",
\"markdown\": {
\"content\": \"仓库: **$REPO**\n结果: **<font color=\\\"$1\\\">$2</font>**\"
}
}" \
"$WEBHOOK_URL"
}
if [ $? -eq 0 ]; then
notify "info" "部署成功 ✓"
else
notify "warning" "部署失败 ✗"
exit 1 # 主动退出非零,让 Action 标记为失败状态
fi
env:
REPO: ${{ github.repository }}
WEBHOOK_URL: ${{ secrets.WECHAT_WEBHOOK_URL }} # 通过 env 传入远程脚本
- name: SSH 步骤本身失败时发送通知
if: failure() # 仅当上面的 step 失败时触发(如 SSH 连接超时、网络问题)
run: |
curl -s -X POST -H 'Content-Type: application/json' \
-d '{
"msgtype": "markdown",
"markdown": {
"content": "GitHub Actions 执行异常,请检查 Actions 日志"
}
}' \
"${{ secrets.WECHAT_WEBHOOK_URL }}"
appleboy/ssh-action的script在服务器上执行,无法直接读取${{ secrets.* }}(那是 Runner 侧的变量)。应通过env字段将需要的值注入为环境变量,再在脚本中用$变量名引用。
NestJS 服务部署(推荐方案:CI 构建镜像 + GHCR)
简化方案在服务器上构建镜像,有三个缺陷:服务器需要安装 Node.js 等构建工具;编译消耗生产服务器资源;镜像没有版本标记,出问题无法快速回滚。
业界标准做法分三步:
- GitHub Runner 构建 Docker 镜像
- 推送到 GitHub Container Registry(GHCR,免费,与仓库权限联动)
- SSH 进服务器,只做
docker compose pull+docker compose up -d,不重新编译
服务器上只需要安装 Docker,Node.js 和 pnpm 不需要。每次部署产生一个带 commit SHA 的镜像版本,回滚只需修改 docker-compose.yml 中的镜像 tag。
前提准备:
- 服务器已安装 Docker
- 已完成 SSH 免密登录配置
- 已添加 Secrets:
SSH_HOST、SSH_USER、SSH_PORT、SSH_PRIVATE_KEY - 仓库 Settings → Actions → General → Workflow permissions 选择
Read and write permissions(允许推送镜像到 GHCR)
服务器上的 docker-compose.yml(部署前提前放好):
yaml
services:
app:
image: ghcr.io/your-org/nest-service:latest # 替换为实际仓库路径
restart: always
ports:
- '3000:3000'
env_file:
- .env # 服务器本地的环境变量文件,存放数据库密码等敏感配置工作流文件 .github/workflows/deploy.yml:
yaml
name: Build and Deploy
on:
push:
branches:
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 登录 GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }} # 自动颁发,无需手动创建
- name: 生成镜像 tag(使用 commit SHA)
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=sha,prefix= # 生成形如 sha-abc1234 的 tag
type=raw,value=latest # 同时打 latest tag
- name: 构建并推送镜像
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
deploy:
runs-on: ubuntu-latest
needs: build # 等镜像推送完成后执行
steps:
- name: SSH 拉取镜像并重启服务
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SSH_HOST }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
port: ${{ secrets.SSH_PORT }}
script: |
echo "$GHCR_TOKEN" | docker login ghcr.io -u "$GHCR_USER" --password-stdin
cd /home/project/nest-service
docker compose pull # 拉取最新镜像
docker compose up -d # 启动新容器(旧容器自动替换)
docker image prune -f # 清理旧镜像,防止磁盘积累
env:
GHCR_USER: ${{ github.actor }}
GHCR_TOKEN: ${{ secrets.GITHUB_TOKEN }}两种方案对比:
| 对比项 | 简化方案(服务器构建) | 推荐方案(CI 构建 + GHCR) |
|---|---|---|
| 服务器需要安装 | Node.js、pnpm、git、Docker | 只需 Docker |
| 构建资源消耗 | 占用生产服务器 | 占用 GitHub Runner(免费) |
| 镜像版本化 | 无 | 每次提交对应一个镜像 tag |
| 回滚 | 需要 git revert 再部署 | 修改 tag 后 docker compose up -d |
| 部署速度 | 慢(服务器上编译) | 快(只拉镜像 + 重启) |
GHCR 的镜像权限跟仓库一致:公开仓库镜像公开可拉取,私有仓库镜像需要登录。如果嫌每次传
GITHUB_TOKEN麻烦,可以在服务器上用docker login ghcr.io提前登录一次(用 PAT),之后就不需要在工作流里每次登录了。
pnpm / npm 依赖缓存加速
每次工作流运行都会重新下载依赖,对于 node_modules 上百 MB 的项目会很慢。通过缓存 pnpm store 或 npm cache,可以把安装时间从 1~2 分钟压缩到几秒钟。
actions/setup-node 内置了 cache 参数,直接启用即可,不需要手动配置 actions/cache:
yaml
- name: 安装 Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm' # 自动缓存 pnpm store;npm 项目改为 'npm'pnpm 项目还需要在这一步之前先安装 pnpm,否则 cache: 'pnpm' 找不到 pnpm 的 store 路径:
yaml
steps:
- uses: actions/checkout@v4
- name: 安装 pnpm
uses: pnpm/action-setup@v4
with:
version: latest
- name: 安装 Node.js(含缓存)
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm' # pnpm 已安装后才能正确找到 store 路径
- name: 安装依赖
run: pnpm install缓存基于 lock 文件(
pnpm-lock.yaml/package-lock.json)的哈希值,lock 文件变动时自动失效并重新下载。
多环境部署策略(dev 分支 → 测试,main 分支 → 生产)
常见的做法是用分支区分环境:main 对应生产服务器,dev 对应测试服务器。同一个工作流文件通过 if 条件和不同的 Secrets 组实现分支路由。
推荐方式:同一个工作流,用 if 区分分支
不同环境的服务器 Secrets 用不同前缀区分(如 PROD_SSH_HOST / DEV_SSH_HOST):
yaml
on:
push:
branches:
- main
- dev
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 部署到生产环境
if: github.ref == 'refs/heads/main' # 只有 main 分支执行
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.PROD_SSH_HOST }}
username: ${{ secrets.PROD_SSH_USER }}
key: ${{ secrets.PROD_SSH_PRIVATE_KEY }}
port: ${{ secrets.PROD_SSH_PORT }}
script: |
cd /home/project/nest-service
git pull origin main
docker compose up -d --build
- name: 部署到测试环境
if: github.ref == 'refs/heads/dev' # 只有 dev 分支执行
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.DEV_SSH_HOST }}
username: ${{ secrets.DEV_SSH_USER }}
key: ${{ secrets.DEV_SSH_PRIVATE_KEY }}
port: ${{ secrets.DEV_SSH_PORT }}
script: |
cd /home/project/nest-service-dev
git pull origin dev
docker compose up -d --build对应需要在 GitHub Secrets 中配置两套变量(PROD_* 和 DEV_*),分别指向两台不同的服务器。
GitHub Pages 静态托管(免服务器)
GitHub Pages 可以直接把仓库的静态文件托管为网站,不需要自己的服务器,适合个人博客、文档站点、开源项目主页。每个 GitHub 账号可以有一个 username.github.io 的主站,仓库也可以单独开启 Pages。
开启方式:
- 进入仓库 → Settings → Pages
- Source 选择
GitHub Actions(推荐,通过工作流控制部署)
工作流文件(以 VitePress 为例,其他框架仅修改构建命令和产物路径):
yaml
name: Deploy to GitHub Pages
on:
push:
branches:
- main
# 必须给 GITHUB_TOKEN 写入权限才能推送到 gh-pages
permissions:
contents: write
pages: write
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: latest
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install
- run: pnpm docs:build # 构建产物输出到 .vitepress/dist/
- name: 上传产物
uses: actions/upload-pages-artifact@v3
with:
path: .vitepress/dist # 普通 Vue 项目改为 dist
- name: 部署到 GitHub Pages
uses: actions/deploy-pages@v4部署成功后,访问地址为 https://用户名.github.io/仓库名/(私有仓库需要 GitHub Pro 或以上才能使用免费 Pages)。
如果使用自定义域名,在仓库 Settings → Pages → Custom domain 填入域名,并在 DNS 服务商处添加 CNAME 记录指向
用户名.github.io。