feat(update): add marked forced updates / 新增标记式强制更新
This commit is contained in:
1 parent
574643a2e9
commit
3e794e123e
5 files changed
+132
-1
No files matched your search
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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 位置 | 默认/示例 |
|
||||
|
||||
@@ -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)。
|
||||
|
||||
## 本地开发
|
||||
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
Reference in new issue
Block a user