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
31 changes: 31 additions & 0 deletions openspec/changes/add-dweb-auto-update/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## Context
- DWEB 需要独立于 Web 的自动升级能力,且必须遵循当前 build 产物路径(stable: /dweb/metadata.json, dev: /dweb-dev/metadata.json)。
- 现有 set-secret 仅管理 secrets,缺少统一的 env/var 描述与本地+GitHub 同步。
- release 流程必须严格基于 origin/main,避免本地工作区干扰,并在分支保护下可用。

## Goals / Non-Goals
- Goals:
- DWEB 自动升级:启动延迟检查 + 关于页手动检查 + 升级弹窗
- 统一配置管理:本地 .env.local 与 GitHub Variables/Secrets
- Release 仅基于 origin/main,必要时 admin 直提
- Non-Goals:
- Web 端升级提示(仅 DWEB)
- 改动现有更新路径与部署策略

## Decisions
- Decision: 通过 Vite define 注入 `__KEYAPP_SITE_ORIGIN__`(来源于 env.SITE_ORIGIN 或构建环境 vars),避免直接暴露 VITE_ 前缀。
- Decision: 更新检测仅在 DWEB 构建中启用(SERVICE_IMPL=dweb),使用 `__DEV_MODE__` 区分 dev/stable 访问路径。
- Decision: metadata.json 解析版本与 changelog(manifest 中 `change_log` 优先),作为弹窗内容来源。
- Decision: set-env 使用 JSON 配置描述字段(类型/显示/本地+GitHub),GitHub 同步分别走 `gh variable` 与 `gh secret`。
- Decision: release 脚本在临时目录 clone origin/main,校验本地与远端一致;如保护分支阻止 push,则允许 `--admin` 直提策略。

## Risks / Trade-offs
- Risk: metadata.json 结构变动导致解析失败 → 失败时仅提示“无法检查更新”,不阻断启动。
- Risk: admin 直提存在权限依赖 → 无权限时明确失败并提示使用管理员账户执行。

## Migration Plan
- 引入 set-env 后,set-secret 保持提示但不再新增字段。
- release 脚本加入 strict 校验;CI/手动流程保持不变。

## Open Questions
- 是否需要把 changelog 展示为可展开的完整内容(默认展示 3–5 行)?
14 changes: 14 additions & 0 deletions openspec/changes/add-dweb-auto-update/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Change: DWEB 自动升级 + 配置工具 + Release 守卫

## Why
DWEB 需要自动检测更新并提供一键升级入口,同时需要统一本地/GitHub 环境配置与发布流程,降低发布心智负担与错误率。

## What Changes
- 新增 DWEB 自动升级:启动延迟检查 + 关于页手动检查 + 升级弹窗 + dweb://install 链接
- 注入 KeyApp 官网 Origin(可配置),根据稳定/测试模式拼接 metadata URL
- 新增 set-env 工具(替代 set-secret):基于配置文件管理本地与 GitHub 的变量/Secrets
- 升级 release 工具:仅允许基于 origin/main 进行发布(必要时使用 admin 方式完成提交)

## Impact
- Affected specs: dweb-update, release-tooling
- Affected code: src/services/* (update service), src/pages/settings/* (About UI), vite.config.ts (define 注入), scripts/set-env.ts, scripts/release.ts
46 changes: 46 additions & 0 deletions openspec/changes/add-dweb-auto-update/specs/dweb-update/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## ADDED Requirements
### Requirement: DWEB Update Source Resolution
The DWEB build SHALL resolve the update metadata URL using the configured site origin and build channel.

#### Scenario: Stable channel metadata URL
- **GIVEN** the app runs in DWEB and `__DEV_MODE__` is false
- **WHEN** resolving the update metadata URL
- **THEN** the path MUST be `/dweb/metadata.json` under the configured site origin

#### Scenario: Dev channel metadata URL
- **GIVEN** the app runs in DWEB and `__DEV_MODE__` is true
- **WHEN** resolving the update metadata URL
- **THEN** the path MUST be `/dweb-dev/metadata.json` under the configured site origin

### Requirement: Automatic Update Check
The DWEB build SHALL perform a delayed update check after startup without blocking initial render.

#### Scenario: Deferred startup check
- **GIVEN** the application has started in DWEB mode
- **WHEN** the delayed timer elapses
- **THEN** the update check runs without blocking initial rendering

### Requirement: Manual Update Check
The user SHALL be able to trigger a manual update check from the Settings > About view.

#### Scenario: User-initiated check
- **GIVEN** the user opens Settings > About
- **WHEN** the user triggers the update check action
- **THEN** the app fetches the update metadata and reports the result

### Requirement: Update Prompt
When a higher version is available, the DWEB build SHALL present an upgrade prompt with a DWEB install link.

#### Scenario: Update available
- **GIVEN** the remote metadata version is higher than the local version
- **WHEN** the update check completes
- **THEN** an upgrade dialog is shown
- **AND** the dialog includes a `dweb://install?url=` link pointing to the metadata URL

### Requirement: Changelog Display
The upgrade dialog SHALL display changelog content when available from metadata/manifest.

#### Scenario: Changelog present
- **GIVEN** update metadata contains a changelog field
- **WHEN** the upgrade dialog is shown
- **THEN** the changelog is displayed in the dialog
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
## ADDED Requirements
### Requirement: Environment Configuration Registry
The project SHALL define a machine-readable environment configuration registry describing each env/secret field.

#### Scenario: Registry includes metadata
- **GIVEN** the registry is loaded
- **WHEN** iterating over fields
- **THEN** each field provides key, type (env/secret), and display policy metadata

### Requirement: set-env Local and GitHub Sync
The set-env tool SHALL support syncing values to local `.env.local`, GitHub Variables, and GitHub Secrets based on the registry.

#### Scenario: Local-only update
- **GIVEN** a field is marked for local sync
- **WHEN** the user saves
- **THEN** `.env.local` is updated with the provided value

#### Scenario: GitHub variable update
- **GIVEN** a field is marked as GitHub variable
- **WHEN** the user syncs to GitHub
- **THEN** `gh variable set` is used to update the repository variable

#### Scenario: GitHub secret update
- **GIVEN** a field is marked as GitHub secret
- **WHEN** the user syncs to GitHub
- **THEN** `gh secret set` is used to update the repository secret

### Requirement: Release Main-Only Guard
The release tool SHALL only run when the target source equals `origin/main`.

#### Scenario: Non-main invocation
- **GIVEN** the release tool is invoked outside `origin/main`
- **WHEN** validation runs
- **THEN** the tool aborts with a clear error

### Requirement: Origin/Main Parity
The release tool SHALL ensure the release is created from the latest `origin/main` without relying on the current working tree state.

#### Scenario: Local working tree differs
- **GIVEN** local changes exist
- **WHEN** release is initiated
- **THEN** the release process uses a clean `origin/main` source and does not modify the local tree

### Requirement: Protected Branch Admin Path
When branch protection blocks direct updates, the release tool SHALL provide an admin path to complete the release.

#### Scenario: Protection blocks direct push
- **GIVEN** branch protection rejects a direct push
- **WHEN** the operator enables admin mode
- **THEN** the release completes using administrator privileges
10 changes: 10 additions & 0 deletions openspec/changes/add-dweb-auto-update/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
## 1. Implementation
- [x] 1.1 增加环境配置描述文件(字段/类型/展示策略/本地+GitHub)
- [x] 1.2 实现 scripts/set-env.ts(替代 set-secret)并保留兼容提示
- [x] 1.3 release 脚本改造:仅允许 origin/main 一致性发布;支持 admin 直接提交
- [x] 1.4 注入 KeyApp 官网 Origin 到运行时常量(非 VITE_ 直接暴露)
- [x] 1.5 新增 DWEB 更新服务(延迟自动检查 + 手动检查)
- [x] 1.6 实现升级弹窗(版本差异 + changelog + 安装链接)
- [x] 1.7 设置页“关于”增加手动检查入口
- [x] 1.8 增加必要的单元测试/最小契约测试
- [x] 1.9 更新文档/白皮书(如需要)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"docs:preview": "vitepress preview docs",
"gen:stable": "bun scripts/gen-stable.ts",
"release": "bun scripts/release.ts",
"set-env": "bun scripts/set-env.ts",
"set-secret": "bun scripts/set-secret.ts",
"gen:logo": "bun scripts/gen-logo.ts",
"i18n:extract": "bun scripts/i18n-extract.ts",
Expand Down
94 changes: 94 additions & 0 deletions scripts/env-registry.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
{
"schemaVersion": 1,
"fields": [
{
"key": "SITE_ORIGIN",
"label": "KeyApp 站点 Origin",
"type": "url",
"required": true,
"targets": { "local": true, "github": "variable" },
"mask": "tail",
"description": "DWEB 自动更新使用的官网域名"
},
{
"key": "DWEB_SFTP_URL",
"label": "DWEB SFTP 地址",
"type": "url",
"required": false,
"targets": { "local": true, "github": "variable" },
"mask": "tail"
},
{
"key": "DWEB_SFTP_USER",
"label": "DWEB SFTP 用户名 (正式)",
"type": "string",
"required": true,
"targets": { "local": true, "github": "secret" },
"mask": "tail"
},
{
"key": "DWEB_SFTP_PASS",
"label": "DWEB SFTP 密码 (正式)",
"type": "password",
"required": true,
"targets": { "local": true, "github": "secret" },
"mask": "hidden"
},
{
"key": "DWEB_SFTP_USER_DEV",
"label": "DWEB SFTP 用户名 (开发)",
"type": "string",
"required": false,
"targets": { "local": true, "github": "secret" },
"mask": "tail"
},
{
"key": "DWEB_SFTP_PASS_DEV",
"label": "DWEB SFTP 密码 (开发)",
"type": "password",
"required": false,
"targets": { "local": true, "github": "secret" },
"mask": "hidden"
},
{
"key": "E2E_TEST_MNEMONIC",
"label": "E2E 测试助记词",
"type": "string",
"required": false,
"targets": { "local": true, "github": "secret" },
"mask": "hidden"
},
{
"key": "E2E_TEST_SECOND_SECRET",
"label": "E2E 安全密码",
"type": "password",
"required": false,
"targets": { "local": true, "github": "secret" },
"mask": "hidden"
},
{
"key": "TRONGRID_API_KEY",
"label": "TronGrid API Key",
"type": "password",
"required": false,
"targets": { "local": true, "github": "secret" },
"mask": "hidden"
},
{
"key": "ETHERSCAN_API_KEY",
"label": "Etherscan API Key",
"type": "password",
"required": false,
"targets": { "local": true, "github": "secret" },
"mask": "hidden"
},
{
"key": "MORALIS_API_KEY",
"label": "Moralis API Key",
"type": "password",
"required": false,
"targets": { "local": true, "github": "secret" },
"mask": "hidden"
}
]
}
Loading