---
url: https://eoht.net/notes/github-actions.md
description: >-
  提供 GitHub Actions 静态站点上传腾讯云 COS、定时快进同步 Fork、Docker 镜像构建与机器人 PR
  自动合并示例，说明分支、构建目录、Secrets、权限和验证方法。
---

# GitHub Actions 工作流程：静态部署、同步 Fork 与构建镜像 {#github-actions-workflows}

本文示例面向 GitHub.com 的 Ubuntu 托管运行器。每段 YAML 是独立工作流，放在目标仓库的 `.github/workflows/` 目录；先替换分支、项目路径和镜像名称，再按需启用。自托管运行器需核对各 Action 的最低运行器要求，不能只把旧版本号替换掉。

## 自动化部署到腾讯云 COS {#deploy-to-tencent-cos}

下面以 npm 管理的 VitePress 项目为例：仓库已提交 `.nvmrc`、`package-lock.json`，`docs:build` 脚本生成 `docs/.vitepress/dist`。使用其他构建工具时，一并替换安装命令、构建脚本和输出目录。

### 获取 API 密钥 {#get-api-credentials}

为发布任务创建独立的腾讯云子用户，授权范围限定到目标存储桶或发布前缀，并按上传、列举等实际操作设置权限。将 SecretId 和 SecretKey 保存到 GitHub Secrets，不写入仓库或日志。

### 获取 COS 桶信息 {#get-cos-bucket-details}

记录存储桶完整名称（含 APPID 后缀）和地域，例如 `ap-shanghai`。另行配置 COS 静态网站、域名和 HTTPS；文件上传成功不代表域名、缓存及访问权限已经正确。

### 配置 Secrets {#configure-secrets}

在仓库的 **Settings → Secrets and variables → Actions** 中创建以下 Secrets，名称与示例保持一致：

| Secret | 内容 |
| --- | --- |
| `SecretId` | 发布子用户的 SecretId |
| `SecretKey` | 对应 SecretKey |
| `Bucket` | 目标存储桶完整名称 |
| `Region` | 存储桶地域 |

### 创建工作流 {#create-workflow}

将以下示例保存为 `.github/workflows/deploy-cos.yml`。默认只发布 `main`，并允许手动触发；`BUILD_DIR` 必须指向构建产物，不能直接上传整个仓库。

```yaml
name: Deploy static site to COS
on:
  push:
    branches: [main]
  workflow_dispatch:
permissions:
  contents: read
concurrency:
  group: deploy-cos
  cancel-in-progress: false
jobs:
  deploy:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    env:
      BUILD_DIR: docs/.vitepress/dist
    steps:
      - uses: actions/checkout@v6
        with:
          fetch-depth: 0
          persist-credentials: false
      - uses: actions/setup-node@v6
        with:
          node-version-file: .nvmrc
          cache: npm
      - run: npm ci
      - run: npm run docs:build
      - name: Check output
        run: test -f "$BUILD_DIR/index.html"
      - uses: actions/setup-python@v6
        with:
          python-version: '3.11'
      - name: Install COSCMD
        run: python -m pip install coscmd
      - name: Upload build output
        env:
          SECRET_ID: ${{ secrets.SecretId }}
          SECRET_KEY: ${{ secrets.SecretKey }}
          BUCKET: ${{ secrets.Bucket }}
          REGION: ${{ secrets.Region }}
        run: |
          coscmd config -a "$SECRET_ID" -s "$SECRET_KEY" -b "$BUCKET" -r "$REGION"
          coscmd upload -r "$BUILD_DIR/" /
```

示例会上传并更新同名对象，保留远端旧文件；删除已下线页面需要另外核对范围和执行清理。首次发布可用专用测试桶，检查首页、深层文章、资源路径和 HTTPS。长期使用时，将通过验证的 COSCMD 版本固定到依赖清单。

参数说明见 [腾讯云 COSCMD 文档](https://cloud.tencent.com/document/product/436/10976)。

## Fork 仓库后定时拉取原项目 Commits {#sync-fork-on-schedule}

如果 Fork 不维护自己的分叉提交，可以用 `gh repo sync` 做快进同步。以下示例不使用 `--force`：分支分叉时会失败并留下日志，便于人工处理。

```yaml
name: Sync fork
on:
  workflow_dispatch:
  schedule:
    - cron: '40 16 * * *'
permissions:
  contents: write
concurrency:
  group: sync-fork
  cancel-in-progress: false
jobs:
  sync:
    if: github.event.repository.fork == true
    runs-on: ubuntu-latest
    steps:
      - name: Fast-forward from parent
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          DESTINATION: ${{ github.repository }}
          SYNC_BRANCH: main
        run: gh repo sync "$DESTINATION" --branch "$SYNC_BRANCH"
```

`40 16 * * *` 是 UTC 16:40，即北京时间次日 00:40。将 `SYNC_BRANCH` 改为父仓库和 Fork 中需要同步的同名分支。工作流需存在于默认分支，Fork 的 Actions 和定时任务也需启用；定时运行不保证精确到秒。

## 自动同步 Fork 上游仓库的适用边界 {#sync-fork-with-upstream}

上面的任务只适合允许直接快进的分支。以下情况应调整流程：

* **Fork 保留独立修改**：先把上游变更引入单独分支，解决冲突并通过 PR 审查，不自动用 `ours` 隐藏冲突。
* **受保护分支要求 PR 或检查**：遵守对应规则，改用同步 PR；不给机器人设置绕过规则的权限。
* **上游改动工作流文件时出现权限错误**：核对 GitHub 对该凭据及文件的授权要求，不直接改成全权限令牌。
* **需要同步后触发下游 CI**：`GITHUB_TOKEN` 产生的普通推送通常不会触发新的工作流；应明确设计后续任务或手动触发，而不假设同步等于部署。

手动运行一次后，对比父仓库与 Fork 的目标提交，再启用计划任务。参考 [gh repo sync](https://cli.github.com/manual/gh_repo_sync) 与 [工作流触发规则](https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow)。

## 自动构建和上传镜像 {#build-and-push-container-image}

以下示例将仓库根目录的 Dockerfile 构建为 Docker Hub 镜像。将 `DOCKERHUB_USERNAME` 设为仓库变量，把有推送权限的访问令牌保存为 `DOCKERHUB_TOKEN` Secret，并替换镜像名中的 `example`。

```yaml
name: Build and push image
on:
  push:
    branches: [main]
  workflow_dispatch:
permissions:
  contents: read
concurrency:
  group: publish-image
  cancel-in-progress: false
jobs:
  image:
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
        with:
          persist-credentials: false
      - uses: docker/setup-buildx-action@v4
      - uses: docker/login-action@v4
        with:
          username: ${{ vars.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}
      - uses: docker/build-push-action@v7
        with:
          context: .
          file: ./Dockerfile
          push: true
          tags: |
            ${{ vars.DOCKERHUB_USERNAME }}/example:latest
            ${{ vars.DOCKERHUB_USERNAME }}/example:${{ github.sha }}
```

构建子目录时同时核对 `context` 与 Dockerfile 路径。这里使用工作区路径作为上下文，构建前生成的文件才会被读取；`.dockerignore` 应排除密钥和无关文件。发布后核对镜像摘要，并在自己的运行环境检查应用能否启动。

参考 [Docker Build Push Action 官方示例](https://github.com/docker/build-push-action)。

## 自动合并拉取请求 {#auto-merge-pull-requests}

先在仓库开启自动合并，并为目标分支配置所需检查和审核规则。下面仅为指定机器人作者的非草稿 PR 启用自动合并；是否最终合并由分支规则决定。

```yaml
name: Enable auto-merge for image bot
on:
  pull_request_target:
    types: [opened, reopened, ready_for_review, synchronize]
permissions:
  contents: write
  pull-requests: write
jobs:
  enable:
    if: github.event.pull_request.user.login == 'imgbot[bot]' && github.event.pull_request.draft == false
    runs-on: ubuntu-latest
    steps:
      - name: Enable auto-merge
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_URL: ${{ github.event.pull_request.html_url }}
          PR_HEAD: ${{ github.event.pull_request.head.sha }}
        run: gh pr merge "$PR_URL" --auto --squash --match-head-commit "$PR_HEAD"
```

此工作流只调用 GitHub API，不检出或运行 PR 分支中的代码。`pull_request_target` 使用目标仓库权限，不能在这里追加来自 PR 的构建脚本。作者白名单只决定是否启用自动合并，不能代替必需检查和审查；没有设置合并条件时，`--auto` 也可能直接合并。

若仓库强制使用合并队列，按队列规则调整合并参数与检查事件。参考 [GitHub 事件说明](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#pull_request_target)、[令牌权限](https://docs.github.com/en/actions/tutorials/authenticate-with-github_token) 和 [gh pr merge](https://cli.github.com/manual/gh_pr_merge)。
