feat(update): add marked forced updates / 新增标记式强制更新

This commit is contained in:
gdz66601 committed 2026-09-23 01:22:25 +08:00
1 parent 574643a2e9
commit 3e794e123e
5 files changed
+132 -1

No files matched your search

+13
View File
@@ -10,6 +10,16 @@ on:
description: Cloudflare 模式的管理员邮箱(也可保存为 ADMIN_EMAIL 变量;GitHub 模式不填)
required: false
type: string
workflow_call:
inputs:
admin_email:
description: Cloudflare 模式的管理员邮箱
required: false
type: string
deployment_ref:
description: 需要检出并部署的提交 SHA
required: false
type: string
concurrency:
group: production-deploy
@@ -40,6 +50,9 @@ jobs:
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
# 强制更新通过可复用工作流传入目标提交,避免推送后仍部署触发任务的旧版本。
ref: ${{ inputs.deployment_ref || github.sha }}
- name: Set up Node.js
uses: actions/setup-node@v4
+84
View File
@@ -0,0 +1,84 @@
name: Force Update
on:
schedule:
- cron: "17 * * * *"
workflow_dispatch:
concurrency:
group: upstream-auto-update
cancel-in-progress: false
permissions:
contents: write
env:
OFFICIAL_REPOSITORY: aozorae/EdgeSSH
OFFICIAL_BRANCH: main
jobs:
update:
name: Check official release markers
runs-on: ubuntu-latest
timeout-minutes: 10
outputs:
target_sha: ${{ steps.update.outputs.target_sha }}
updated: ${{ steps.update.outputs.updated }}
steps:
- name: Check out user repository
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- name: Find marked commit and update main
id: update
shell: bash
run: |
git remote add official "https://github.com/${OFFICIAL_REPOSITORY}.git"
git fetch --no-tags official "${OFFICIAL_BRANCH}"
current_sha="$(git rev-parse refs/remotes/origin/main)"
official_sha="$(git rev-parse "refs/remotes/official/${OFFICIAL_BRANCH}")"
target_sha=""
# rev-list 按拓扑从新到旧扫描,只接受完整 Git trailer,防止正文中的同名文字误触发更新。
while read -r sha; do
marker="$(git show -s --format='%(trailers:key=EdgeSSH-Auto-Update,valueonly,unfold)' "${sha}")"
if [[ "${marker}" == "true" ]]; then
target_sha="${sha}"
break
fi
done < <(git rev-list --topo-order "refs/remotes/official/${OFFICIAL_BRANCH}" --not "${current_sha}")
if [[ -z "${target_sha}" ]]; then
echo "updated=false" >> "${GITHUB_OUTPUT}"
{
echo "## Force Update"
echo
echo "No commit marked \`EdgeSSH-Auto-Update: true\` exists between \`${current_sha}\` and \`${official_sha}\`."
} >> "${GITHUB_STEP_SUMMARY}"
exit 0
fi
# lease 保证检测期间 main 若被人工更新则停止,不覆盖刚产生的新提交;下次定时任务会重新计算。
git push origin "${target_sha}:refs/heads/main" \
--force-with-lease="refs/heads/main:${current_sha}"
echo "target_sha=${target_sha}" >> "${GITHUB_OUTPUT}"
echo "updated=true" >> "${GITHUB_OUTPUT}"
{
echo "## Force Update"
echo
echo "Updated \`main\` from \`${current_sha}\` to marked commit \`${target_sha}\`."
echo "The deployment job will deploy that exact commit in this run."
} >> "${GITHUB_STEP_SUMMARY}"
deploy:
name: Deploy marked commit
needs: update
if: needs.update.outputs.updated == 'true'
uses: ./.github/workflows/deploy.yml
with:
deployment_ref: ${{ needs.update.outputs.target_sha }}
secrets: inherit
+12
View File
@@ -75,6 +75,18 @@ API Token 只存 GitHub Secret,不放普通变量、代码或命令行输入
设置自定义域名时关闭备用 `workers.dev` 入口,所有部署关闭 preview URL。Cloudflare 模式校验 Access JWT;GitHub 模式使用 state、PKCE 和签名 HttpOnly Cookie。缺少认证不降级为匿名 SSH。
## 官方强制更新
Fork 中的 `Force Update` 工作流每小时第 17 分钟运行,也支持从 Actions 页面手动运行。它会获取官方 `aozorae/EdgeSSH` 的 `main`,扫描 Fork 当前 `main` 到官方最新版本之间的提交,并查找以下完整 Git trailer:
```text
EdgeSSH-Auto-Update: true
```
不存在标记时,工作流成功结束且不改代码、不部署。存在标记时,它选择拓扑顺序中最新的标记提交,以 `force-with-lease` 将 Fork 的 `main` 精确更新到该 SHA,然后直接调用 `Deploy` 的可复用部署任务并检出同一个 SHA。部署不依赖这次推送再次触发工作流,因此不会受 GitHub 防递归机制影响。
此能力用于维护者发布必须尽快应用的安全或兼容性更新。精确同步会移除 Fork 在 `main` 上独有的提交;需要长期维护的自定义改动应放在其他分支。若检测期间 `main` 又被人工更新,lease 会让本次任务停止,下一次运行会基于新版本重新检查。仓库或组织策略还必须允许工作流使用 `contents: write`,否则无法更新分支。
## 其他可选配置
| 名称 | GitHub 位置 | 默认/示例 |
+5 -1
View File
@@ -162,7 +162,11 @@ npm ci
`ENCRYPTION_KEY` 由部署流程管理并持久保存在 **Cloudflare Worker Secrets**;Cloudflare 模式另存 `ACCESS_TEAM_DOMAIN`、`ACCESS_AUD`,GitHub 模式同步 `GITHUB_CLIENT_SECRET`。不需要用户复制自动生成的值回 GitHub。加密密钥只在首次部署生成,后续保留,即使 GitHub 留有旧值也不会覆盖线上密钥。不要删除 Worker 或其加密密钥;Cloudflare 不提供密钥明文读回,丢失后无法解密已有资料。
推送 `main` 或手动运行 `Deploy` 都会执行检查和部署。只在首次 Run workflow 输入邮箱也可以:后续未提供邮箱时保留已有认证配置。需要更换域名或重新自动配置 Access 时,请再次提供邮箱。完整权限、旧版迁移及 GitHub 登录扩展说明见[部署指南](DEPLOYMENT.md);手工维护见 [Zero Trust 指南](docs/ZERO_TRUST.md)。
推送 `main` 或手动运行 `Deploy` 都会执行检查和部署。只在首次 Run workflow 输入邮箱也可以:后续未提供邮箱时保留已有认证配置。需要更换域名或重新自动配置 Access 时,请再次提供邮箱。
Fork 启用 Actions 后,`Force Update` 每小时检查一次官方 `aozorae/EdgeSSH` 的 `main`。只有当前版本与官方最新版本之间出现带有独立 Git trailer `EdgeSSH-Auto-Update: true` 的提交时,工作流才会将 Fork 的 `main` 精确同步到最新一个标记提交,并在同一次运行中直接部署该 SHA;普通提交不会触发同步。也可从 Actions 页面手动运行检查。该流程会覆盖 Fork 在 `main` 上的自定义提交,自定义开发请保留在其他分支。
完整权限、自动更新、旧版迁移及 GitHub 登录扩展说明见[部署指南](DEPLOYMENT.md);手工维护见 [Zero Trust 指南](docs/ZERO_TRUST.md)。
## 本地开发
+18
View File
@@ -202,3 +202,21 @@ test('Actions asks for email, keeps Token private and uses the shared deploy ent
assert.ok(workflow.includes('run: npm run deploy\n'));
assert.equal(workflow.includes('wrangler d1 migrations apply'), false);
});
test('Force Update selects a marked official commit and deploys the same SHA directly', async () => {
const deploy = await readFile(new URL('../.github/workflows/deploy.yml', import.meta.url), 'utf8');
const update = await readFile(new URL('../.github/workflows/force-update.yml', import.meta.url), 'utf8');
assert.ok(deploy.includes('workflow_call:'));
assert.ok(deploy.includes('deployment_ref:'));
assert.ok(deploy.includes("ref: ${{ inputs.deployment_ref || github.sha }}"));
assert.ok(update.includes('OFFICIAL_REPOSITORY: aozorae/EdgeSSH'));
assert.ok(update.includes('fetch-depth: 0'));
assert.ok(update.includes("trailers:key=EdgeSSH-Auto-Update,valueonly,unfold"));
assert.ok(update.includes('--not "${current_sha}"'));
assert.ok(update.includes('--force-with-lease="refs/heads/main:${current_sha}"'));
assert.ok(update.includes('uses: ./.github/workflows/deploy.yml'));
assert.ok(update.includes('deployment_ref: ${{ needs.update.outputs.target_sha }}'));
assert.equal(update.includes('npm run deploy'), false);
});