Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1333,6 +1333,18 @@ jobs:
make_latest: ${{ contains(github.ref_name, '-') && 'false' || 'true' }}
files: release-assets/*

refresh-public-downloads:
name: refresh public downloads
if: startsWith(github.ref, 'refs/tags/v')
needs: release
uses: ./.github/workflows/refresh-public-downloads.yml
permissions:
contents: read
with:
release_tag: ${{ github.ref_name }}
secrets:
PUBLIC_SITE_DEPLOY_KEY: ${{ secrets.PUBLIC_SITE_DEPLOY_KEY }}

publish-npm:
name: publish npm
# npm publication is paused for both tagged releases and manual backfills.
Expand Down
96 changes: 96 additions & 0 deletions .github/workflows/refresh-public-downloads.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: refresh-public-downloads

on:
workflow_call:
inputs:
release_tag:
required: true
type: string
secrets:
PUBLIC_SITE_DEPLOY_KEY:
required: true
workflow_dispatch:
inputs:
release_tag:
description: Existing published release tag to verify and synchronize
required: true
type: string

permissions:
contents: read

concurrency:
group: public-download-catalog
cancel-in-progress: false

jobs:
synchronize:
runs-on: ubuntu-latest
timeout-minutes: 25
steps:
- name: Validate release tag and deployment credential
env:
RELEASE_TAG: ${{ inputs.release_tag }}
DEPLOY_KEY: ${{ secrets.PUBLIC_SITE_DEPLOY_KEY }}
shell: bash
run: |
set -euo pipefail
[[ "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$ ]] || {
echo "::error::A published v<version> tag is required"
exit 1
}
test -n "$DEPLOY_KEY" || {
echo "::error::PUBLIC_SITE_DEPLOY_KEY is not configured"
exit 1
}

- name: Checkout public website
uses: actions/checkout@v4
with:
repository: tmoonlight/acecode-site
ref: main
ssh-key: ${{ secrets.PUBLIC_SITE_DEPLOY_KEY }}
fetch-depth: 1

- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: actions/setup-node@v4
with:
node-version: "22"

- name: Synchronize published downloads and trigger Pages
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.release_tag }}
shell: bash
run: |
set -euo pipefail
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
for attempt in 1 2 3; do
# This is the disposable CI website checkout. Refresh against main
# after a concurrent website edit, then regenerate only public data.
git fetch origin main
git checkout -B catalog-sync origin/main
python scripts/sync_catalog.py --require-tag "$RELEASE_TAG" --releases-only
python -m unittest discover -s scripts -p "test_*.py"
node scripts/check_download.mjs
git add public/aceupdate.json public/data/release-source.json
git diff --cached --check
if git diff --cached --quiet; then
echo "Public catalog already matches GitHub Releases"
exit 0
fi
git commit -m "Refresh download catalog after $RELEASE_TAG"
if git push origin HEAD:main; then
echo "Public website updated; Pages deployment is triggered by this push"
exit 0
fi
echo "Website main changed during synchronization; retry $attempt/3"
done
echo "::error::Could not update the public website"
exit 1

- name: Verify public CDN catalog
run: python scripts/wait_for_catalog.py public/aceupdate.json --timeout 900
20 changes: 20 additions & 0 deletions docs/public-download-publishing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 公开下载站自动更新

GitHub 标签发布成功后,`package.yml` 自动调用
`refresh-public-downloads.yml`,更新 `tmoonlight/acecode-site` 的下载清单。
网站仓库的 push 自动触发 Pages 部署,发布任务继续等待 CDN 清单内容一致。
正常发布无需在本机执行网站刷新命令。

同步只接受已公开且核心安装资产及 SHA-256 完整的版本。普通分支构建、
失败的 Release 发布不会触发网站更新。重试会读取全部公开 Releases,
保留最新版本;相同内容不产生空提交。

跨仓库写入使用 `PUBLIC_SITE_DEPLOY_KEY` Actions Secret,对应部署密钥
仅授权 `acecode-site` 仓库。不要在代码、日志或普通变量中放置私钥。

流水线的网站同步失败时,已发布的安装包不回滚。检查同步任务和网站仓库
`Deploy Pages` 的失败原因,修复后重跑失败任务。维护时也可在 ACECode
仓库 Actions 中选择 `refresh-public-downloads`,输入已有 `v<version>` 标签。
CDN 使用短缓存,工作流最多等待 15 分钟确认公开目录。

该步骤不替代原有的本机 aupdate 完整镜像与客户端升级验证。
2 changes: 2 additions & 0 deletions openspec/changes/auto-refresh-public-downloads/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-10-20
39 changes: 39 additions & 0 deletions openspec/changes/auto-refresh-public-downloads/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## Context

`package.yml` 通过工作流令牌调用 GitHub Release 发布;该令牌产生的普通事件
不能用作另一个工作流的可靠触发器。下载站位于独立公开仓库
`tmoonlight/acecode-site`,已有 push 驱动的 Pages 工作流。

## Goals / Non-Goals

目标是将目录更新、Pages 部署与 CDN 验证串联为发布后续步骤。
不改变安装包构建矩阵、版本生成、用户中心或本机 aupdate 镜像。

## Decisions

1. 在 `package.yml` 添加 `needs: release` 的可复用工作流调用。
不依赖由工作流令牌生成的 `release.published` 事件,也不使用定时轮询。
2. 新工作流支持 `workflow_call` 和维护用 `workflow_dispatch`,输入明确标签。
它检查独立网站仓库 main,生成公开目录、校验、精确暂存并 push。
3. 网站仓库使用专用 SSH 写入部署密钥;私钥仅存 ACECode Actions Secret。
其权限局限于网站仓库,ACECode 的工作流令牌仅保留读取权限。
4. 清单生成器提供只同步 Release 与必需标签校验参数,避免此步骤依赖价格服务。
记录目标版本公开来源,首次接入可用现有版本验证整条链路。
5. 网站 push 自动部署 Pages。同步工作流比较 CDN `aceupdate.json` 的 SHA-256,
最多等待 15 分钟,覆盖 Pages 构建时间与已有 5 分钟边缘缓存。
相同内容不创建空提交,但仍验证线上目录。
6. 同步工作流串行执行,push 遇到外部并发更新重新拉取并生成,最多重试三次。
仅刷新清单及来源记录,不推送价格数据或其他页面内容。

## Risks / Trade-offs

- Pages 或 GitHub 故障 → 同步步骤失败,保留已发布资产,支持重跑。
- 网站清单已更新但 Pages 上一次部署失败 → 幂等同步不制造空提交;修复网站工作流后重跑 Pages,再重跑验证。
- 两次发布快速连续完成 → 每次读取完整公开 Releases,避免覆盖最新版本。
- 密钥失效 → Actions 显示认证失败,替换网站部署密钥及对应 Secret。

## Migration Plan

先发布网站生成器与测试,再配置专用密钥,通过 PR 将 ACECode 工作流合入 master。
用当前已发布版本调用同一可复用工作流,验证网站 push 自动启动 Pages 及 CDN 内容。
回退时移除发布后的工作流调用并撤销该部署密钥;既有下载站继续工作。
27 changes: 27 additions & 0 deletions openspec/changes/auto-refresh-public-downloads/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
## Why

ACECode 安装包发布后,公开下载站目前需要人工运行 Pages 工作流。
将刷新接入发布成功后的流程,使下载目录在本机离线时也能自动更新。

## What Changes

- `package.yml` 的 Release 发布成功后调用独立的网站同步工作流。
- 同步工作流通过仅限 `acecode-site` 的部署密钥更新公开清单并触发 Pages。
- 校验目标版本已发布且安装资产具有校验值;重复执行不创建空提交。
- 同步后等待 CDN 公开清单与提交内容一致,失败在发布流水线中明确显示。

## Capabilities

### New Capabilities

- `public-release-catalog`: 公开下载清单随 GitHub Release 发布自动同步、部署和验证。

### Modified Capabilities

无。

## Impact

涉及 ACECode 的 GitHub Actions 和发布文档,以及独立仓库
`tmoonlight/acecode-site` 的清单生成器、测试与部署文档。
本机 aupdate 镜像流程、用户中心和安装包构建行为保持原有约定。
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
## Purpose

让 ACECode 的公开下载目录随安装包发布自动更新,用户无需在本机执行刷新命令。
目录只展示已公开的版本资产,并在部署后验证 CDN 实际内容,保证发布记录与用户看到的文件一致。

## ADDED Requirements

### Requirement: 发布成功后自动更新下载站

系统 SHALL 在 GitHub Release 安装资产发布成功后自动刷新并部署公开下载清单。

#### Scenario: 标签发布成功
- **WHEN** 标签对应的安装包发布步骤成功
- **THEN** 自动执行公开目录同步和 Pages 部署
- **AND** 全过程不依赖用户电脑开机

#### Scenario: 发布失败或非标签打包
- **WHEN** 安装包发布失败或运行普通分支打包
- **THEN** 不执行本次发布的网站同步

### Requirement: 验证公开版本与同步结果

系统 SHALL 拒绝缺失、草稿或没有完整下载校验值的目标版本,并在有限时间内确认 CDN 清单与生成结果一致。

#### Scenario: 无效目标
- **WHEN** 请求同步的版本未公开或资产缺少有效 SHA-256
- **THEN** 同步失败且不推送不完整目录

#### Scenario: CDN 尚未更新
- **WHEN** Pages 部署失败或 CDN 未在等待期限内提供目标目录
- **THEN** 发布流程的网站同步步骤明确失败,已有安装包与旧网页继续可用

### Requirement: 重试与授权边界

系统 SHALL 只使用公开站点仓库的专用写入授权,串行处理同步并允许同版本安全重试。

#### Scenario: 相同内容重复执行
- **WHEN** 重新运行同一版本同步且目录内容未变
- **THEN** 不创建空提交,并继续验证 CDN 目录

#### Scenario: 旧版本重新运行
- **WHEN** 已有更新版本后重试旧版本同步
- **THEN** 仍读取所有已公开 Release,不能把当前下载目录回退到旧版本
15 changes: 15 additions & 0 deletions openspec/changes/auto-refresh-public-downloads/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
## 1. 网站同步能力

- [x] 1.1 增加必需标签校验与仅刷新 Release 模式,单元测试覆盖缺失、未完整发布及幂等内容。
- [x] 1.2 更新网站部署说明并通过现有下载清单测试,发布网站仓库变更。

## 2. 发布衔接

- [x] 2.1 添加可复用工作流及 package 发布后的调用,完成工作流语法与依赖检查,补齐发布说明。
- [x] 2.2 配置网站专用部署密钥和 ACECode Secret,验证授权限定目标仓库。
- [ ] 2.3 通过 PR 合入 master,精确提交本任务文件并确认其他未提交内容保持。

## 3. 联调

- [ ] 3.1 用现有 Release 执行同步,确认自动 push 触发 Pages,并验证 CDN 清单内容。
- [ ] 3.2 重复执行同版本确认没有空提交,记录成功运行链接及严格 OpenSpec 校验。
Loading