|
|
|
|
|
# Velopack + Supabase Storage 自动更新复用指南
|
|
|
|
|
|
|
|
|
|
|
|
本文档用于在桌面程序中复用“Velopack 生成安装包和更新包,Supabase Storage 托管更新源”的自动更新流程。示例均使用占位符,不绑定任何具体项目。
|
|
|
|
|
|
|
|
|
|
|
|
## 方案概览
|
|
|
|
|
|
|
|
|
|
|
|
更新链路分为两部分:
|
|
|
|
|
|
|
|
|
|
|
|
1. 发布端使用 Velopack CLI 将程序发布目录打包成安装包、全量包、差分包和 `releases.win.json`。
|
|
|
|
|
|
2. 上传端将这些产物放到 Supabase Storage 的公开桶目录中。
|
|
|
|
|
|
3. 客户端运行时只读取公开更新源 URL,调用 Velopack 检查、下载并应用更新。
|
|
|
|
|
|
|
|
|
|
|
|
客户端不需要 Supabase key。Supabase key 只应出现在发布脚本、CI/CD 或受信任的维护机器中。
|
|
|
|
|
|
|
|
|
|
|
|
## Supabase 配置
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 创建 Storage 桶
|
|
|
|
|
|
|
|
|
|
|
|
推荐使用一个专门存放发布包的公开 File bucket:
|
|
|
|
|
|
|
|
|
|
|
|
| 配置项 | 推荐值 | 说明 |
|
|
|
|
|
|
| --- | --- | --- |
|
|
|
|
|
|
| Bucket name | `app-releases` | 可按组织统一命名 |
|
|
|
|
|
|
| Public bucket | `true` | 客户端需要匿名下载 `releases.win.json` 和安装包 |
|
|
|
|
|
|
| File size limit | 按安装包大小设置 | 例如 `512MB`、`1GB` 或更高 |
|
|
|
|
|
|
| Allowed MIME types | 留空或允许二进制 | Velopack 产物包含 `.exe`、`.nupkg`、`.json` 等 |
|
|
|
|
|
|
|
|
|
|
|
|
Dashboard 路径通常是:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
Supabase Dashboard -> Storage -> New bucket
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
也可以用 SQL 创建桶:
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
insert into storage.buckets (
|
|
|
|
|
|
id,
|
|
|
|
|
|
name,
|
|
|
|
|
|
public,
|
|
|
|
|
|
file_size_limit,
|
|
|
|
|
|
allowed_mime_types
|
|
|
|
|
|
)
|
|
|
|
|
|
values (
|
|
|
|
|
|
'app-releases',
|
|
|
|
|
|
'app-releases',
|
|
|
|
|
|
true,
|
|
|
|
|
|
1073741824,
|
|
|
|
|
|
null
|
|
|
|
|
|
)
|
|
|
|
|
|
on conflict (id) do update
|
|
|
|
|
|
set
|
|
|
|
|
|
public = excluded.public,
|
|
|
|
|
|
file_size_limit = excluded.file_size_limit,
|
|
|
|
|
|
allowed_mime_types = excluded.allowed_mime_types;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 目录规划
|
|
|
|
|
|
|
|
|
|
|
|
建议每个应用、平台和发布通道使用独立目录:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
app-releases/
|
|
|
|
|
|
my-app/
|
|
|
|
|
|
windows/
|
|
|
|
|
|
stable/
|
|
|
|
|
|
releases.win.json
|
|
|
|
|
|
RELEASES
|
|
|
|
|
|
MyApp-win-Setup.exe
|
|
|
|
|
|
MyApp-1.2.3-full.nupkg
|
|
|
|
|
|
MyApp-1.2.3-delta.nupkg
|
|
|
|
|
|
beta/
|
|
|
|
|
|
releases.win.json
|
|
|
|
|
|
...
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
对应的 Velopack 更新源根地址为:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
https://<project-ref>.supabase.co/storage/v1/object/public/app-releases/my-app/windows/stable
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Velopack 会在该根地址下读取 `releases.win.json`,所以不要把 `releases.win.json` 再放到额外子目录里。
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 权限策略
|
|
|
|
|
|
|
|
|
|
|
|
推荐做法:
|
|
|
|
|
|
|
|
|
|
|
|
- 桶设为 public,只开放匿名读取。
|
|
|
|
|
|
- 发布上传使用 `service_role` key,并且只在 CI/CD 或维护机器环境变量中保存。
|
|
|
|
|
|
- 不给 `anon` 角色配置 `INSERT`、`UPDATE`、`DELETE` 权限。
|
|
|
|
|
|
- 不在客户端配置、日志、提交信息或文档中写入真实 key。
|
|
|
|
|
|
|
|
|
|
|
|
Supabase 的 public bucket 只代表对象可以通过公开 URL 读取;上传、覆盖、删除仍受访问控制影响。使用 `service_role` key 上传时会绕过 RLS,适合发布流程。
|
|
|
|
|
|
|
|
|
|
|
|
如果必须用非 service role 的登录用户上传,需要在 `storage.objects` 上创建受限策略。下面示例只允许已登录用户写入指定目录,实际项目应再结合用户、角色或后端校验收紧权限:
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
create policy "release upload insert"
|
|
|
|
|
|
on storage.objects
|
|
|
|
|
|
for insert
|
|
|
|
|
|
to authenticated
|
|
|
|
|
|
with check (
|
|
|
|
|
|
bucket_id = 'app-releases'
|
|
|
|
|
|
and (storage.foldername(name))[1] = 'my-app'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
create policy "release upload select"
|
|
|
|
|
|
on storage.objects
|
|
|
|
|
|
for select
|
|
|
|
|
|
to authenticated
|
|
|
|
|
|
using (
|
|
|
|
|
|
bucket_id = 'app-releases'
|
|
|
|
|
|
and (storage.foldername(name))[1] = 'my-app'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
create policy "release upload update"
|
|
|
|
|
|
on storage.objects
|
|
|
|
|
|
for update
|
|
|
|
|
|
to authenticated
|
|
|
|
|
|
using (
|
|
|
|
|
|
bucket_id = 'app-releases'
|
|
|
|
|
|
and (storage.foldername(name))[1] = 'my-app'
|
|
|
|
|
|
)
|
|
|
|
|
|
with check (
|
|
|
|
|
|
bucket_id = 'app-releases'
|
|
|
|
|
|
and (storage.foldername(name))[1] = 'my-app'
|
|
|
|
|
|
);
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
覆盖已有文件需要 `SELECT` 和 `UPDATE` 权限。发布流程更推荐使用 service role,而不是给普通客户端发放写权限。
|
|
|
|
|
|
|
|
|
|
|
|
## 应用配置文件
|
|
|
|
|
|
|
|
|
|
|
|
建议在应用中放一个独立配置文件,例如 `Config/update_config.json`:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
{
|
|
|
|
|
|
"enable_auto_update": true,
|
|
|
|
|
|
"update_server_type": "supabase",
|
|
|
|
|
|
"update_server_url": "https://<project-ref>.supabase.co/storage/v1/object/public/app-releases/my-app/windows/stable",
|
|
|
|
|
|
"update_check_interval_hours": 24,
|
|
|
|
|
|
"silent_install": false,
|
|
|
|
|
|
"supabase": {
|
|
|
|
|
|
"project_url": "https://<project-ref>.supabase.co",
|
|
|
|
|
|
"bucket_name": "app-releases",
|
|
|
|
|
|
"directory": "my-app/windows/stable"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
字段说明:
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 用途 |
|
|
|
|
|
|
| --- | --- |
|
|
|
|
|
|
| `enable_auto_update` | 是否启用自动更新 |
|
|
|
|
|
|
| `update_server_type` | 可固定为 `supabase`,用于程序内部区分更新源 |
|
|
|
|
|
|
| `update_server_url` | 客户端 Velopack 更新源根地址 |
|
|
|
|
|
|
| `update_check_interval_hours` | 自动检查间隔;手动检查可忽略该间隔 |
|
|
|
|
|
|
| `silent_install` | 是否下载后延迟到下次启动自动应用 |
|
|
|
|
|
|
| `supabase.project_url` | 发布脚本上传时使用的 Supabase 项目 URL |
|
|
|
|
|
|
| `supabase.bucket_name` | 发布包所在 bucket |
|
|
|
|
|
|
| `supabase.directory` | bucket 内目录 |
|
|
|
|
|
|
|
|
|
|
|
|
`supabase` 节只给发布脚本使用。客户端运行时检查更新只需要 `update_server_url`。
|
|
|
|
|
|
|
|
|
|
|
|
## 客户端集成
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 安装 NuGet 包
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
dotnet add <YourApp.csproj> package Velopack
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 在程序入口尽早初始化
|
|
|
|
|
|
|
|
|
|
|
|
Velopack 初始化应尽量放在应用启动最早位置:
|
|
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
using Velopack;
|
|
|
|
|
|
|
|
|
|
|
|
internal static class Program
|
|
|
|
|
|
{
|
|
|
|
|
|
[STAThread]
|
|
|
|
|
|
public static void Main(string[] args)
|
|
|
|
|
|
{
|
|
|
|
|
|
VelopackApp.Build()
|
|
|
|
|
|
.SetArgs(args)
|
|
|
|
|
|
.SetAutoApplyOnStartup(true)
|
|
|
|
|
|
.Run();
|
|
|
|
|
|
|
|
|
|
|
|
StartApplication(args);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`SetAutoApplyOnStartup(true)` 用于在下次启动时自动应用已下载但尚未安装的更新。
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 检查、下载并应用更新
|
|
|
|
|
|
|
|
|
|
|
|
下面是最小可复用逻辑:
|
|
|
|
|
|
|
|
|
|
|
|
```csharp
|
|
|
|
|
|
using Velopack;
|
|
|
|
|
|
|
|
|
|
|
|
public sealed class AppUpdateService
|
|
|
|
|
|
{
|
|
|
|
|
|
private readonly string _updateServerUrl;
|
|
|
|
|
|
|
|
|
|
|
|
public AppUpdateService(string updateServerUrl)
|
|
|
|
|
|
{
|
|
|
|
|
|
_updateServerUrl = updateServerUrl;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<bool> CheckDownloadAndRestartAsync(
|
|
|
|
|
|
IProgress<int>? progress = null,
|
|
|
|
|
|
CancellationToken cancellationToken = default)
|
|
|
|
|
|
{
|
|
|
|
|
|
var manager = new UpdateManager(_updateServerUrl);
|
|
|
|
|
|
if (!manager.IsInstalled)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var updateInfo = await manager.CheckForUpdatesAsync().ConfigureAwait(false);
|
|
|
|
|
|
if (updateInfo?.TargetFullRelease is null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
await manager.DownloadUpdatesAsync(
|
|
|
|
|
|
updateInfo,
|
|
|
|
|
|
value => progress?.Report(Math.Clamp(value, 0, 100)),
|
|
|
|
|
|
cancellationToken).ConfigureAwait(false);
|
|
|
|
|
|
|
|
|
|
|
|
manager.ApplyUpdatesAndRestart(updateInfo.TargetFullRelease);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
建议在 UI 层:
|
|
|
|
|
|
|
|
|
|
|
|
- 启动后延迟 1 到 3 秒检查,避免影响首屏。
|
|
|
|
|
|
- 发现新版本后先提示用户,再下载并重启。
|
|
|
|
|
|
- 手动“检查更新”按钮忽略检查间隔。
|
|
|
|
|
|
- 开发环境或直接运行 `dotnet run` 时,`manager.IsInstalled` 通常为 `false`,应跳过更新检查。
|
|
|
|
|
|
|
|
|
|
|
|
## 打包发布
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 安装 Velopack CLI
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
dotnet tool install -g vpk
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 提升版本号
|
|
|
|
|
|
|
|
|
|
|
|
更新项目文件中的版本号,确保新版本高于线上版本:
|
|
|
|
|
|
|
|
|
|
|
|
```xml
|
|
|
|
|
|
<Version>1.2.3</Version>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 发布应用目录
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
dotnet publish .\src\MyApp\MyApp.csproj `
|
|
|
|
|
|
-c Release `
|
|
|
|
|
|
-r win-x64 `
|
|
|
|
|
|
--self-contained false `
|
|
|
|
|
|
-o .\artifacts\publish\my-app
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
如果目标机器不保证安装 .NET Runtime,可以改用 self-contained 发布,或按 Velopack 文档为安装包配置所需 framework。
|
|
|
|
|
|
|
|
|
|
|
|
### 4. 生成 Velopack 包
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
vpk pack `
|
|
|
|
|
|
--packId MyCompany.MyApp `
|
|
|
|
|
|
--packVersion 1.2.3 `
|
|
|
|
|
|
--packDir .\artifacts\publish\my-app `
|
|
|
|
|
|
--mainExe MyApp.exe `
|
|
|
|
|
|
--packTitle "My App" `
|
|
|
|
|
|
--packAuthors "MyCompany" `
|
|
|
|
|
|
--outputDir .\artifacts\releases\my-app
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
输出目录应至少包含:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
MyApp-win-Setup.exe
|
|
|
|
|
|
MyCompany.MyApp-1.2.3-full.nupkg
|
|
|
|
|
|
MyCompany.MyApp-1.2.3-delta.nupkg
|
|
|
|
|
|
releases.win.json
|
|
|
|
|
|
RELEASES
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
首次发布可能没有 delta 包;从第二次发布开始应包含 delta 包。
|
|
|
|
|
|
|
|
|
|
|
|
## 上传到 Supabase Storage
|
|
|
|
|
|
|
|
|
|
|
|
发布脚本应读取配置文件中的 `supabase.project_url`、`supabase.bucket_name` 和 `supabase.directory`,并从环境变量读取服务端 key:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
$env:SUPABASE_SERVICE_ROLE_KEY = "<service-role-key>"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
通用上传脚本示例:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
param(
|
|
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
|
|
|
|
[string]$ConfigPath,
|
|
|
|
|
|
|
|
|
|
|
|
[Parameter(Mandatory = $true)]
|
|
|
|
|
|
[string]$ReleasePath,
|
|
|
|
|
|
|
|
|
|
|
|
[string]$ServiceRoleKey = $env:SUPABASE_SERVICE_ROLE_KEY
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
|
|
|
|
|
|
|
|
function Join-UrlPath {
|
|
|
|
|
|
param(
|
|
|
|
|
|
[string]$Left,
|
|
|
|
|
|
[string]$Right
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
if ([string]::IsNullOrWhiteSpace($Left)) {
|
|
|
|
|
|
return $Right
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ([string]::IsNullOrWhiteSpace($Right)) {
|
|
|
|
|
|
return $Left.TrimEnd("/")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return $Left.TrimEnd("/") + "/" + $Right.TrimStart("/")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ([string]::IsNullOrWhiteSpace($ServiceRoleKey)) {
|
|
|
|
|
|
throw "SUPABASE_SERVICE_ROLE_KEY is empty."
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$config = Get-Content -Encoding UTF8 $ConfigPath -Raw | ConvertFrom-Json
|
|
|
|
|
|
$baseUrl = $config.supabase.project_url.TrimEnd("/")
|
|
|
|
|
|
$bucketName = $config.supabase.bucket_name
|
|
|
|
|
|
$directory = $config.supabase.directory
|
|
|
|
|
|
|
|
|
|
|
|
if ([string]::IsNullOrWhiteSpace($baseUrl) -or
|
|
|
|
|
|
[string]::IsNullOrWhiteSpace($bucketName) -or
|
|
|
|
|
|
[string]::IsNullOrWhiteSpace($directory)) {
|
|
|
|
|
|
throw "Supabase project_url, bucket_name, or directory is missing."
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$files = Get-ChildItem -Path $ReleasePath -File | Sort-Object Name
|
|
|
|
|
|
if (-not $files) {
|
|
|
|
|
|
throw "No release files were found."
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$headers = @{
|
|
|
|
|
|
"apikey" = $ServiceRoleKey
|
|
|
|
|
|
"Authorization" = "Bearer $ServiceRoleKey"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
foreach ($file in $files) {
|
|
|
|
|
|
$objectPath = Join-UrlPath $directory $file.Name
|
|
|
|
|
|
$uploadUrl = Join-UrlPath "$baseUrl/storage/v1/object/$bucketName" $objectPath
|
|
|
|
|
|
|
|
|
|
|
|
$uploadHeaders = $headers.Clone()
|
|
|
|
|
|
$uploadHeaders["Content-Type"] = "application/octet-stream"
|
|
|
|
|
|
$uploadHeaders["x-upsert"] = "true"
|
|
|
|
|
|
|
|
|
|
|
|
Invoke-RestMethod `
|
|
|
|
|
|
-Uri $uploadUrl `
|
|
|
|
|
|
-Method Post `
|
|
|
|
|
|
-Headers $uploadHeaders `
|
|
|
|
|
|
-InFile $file.FullName
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$publicBaseUrl = Join-UrlPath "$baseUrl/storage/v1/object/public/$bucketName" $directory
|
|
|
|
|
|
Write-Host "Public feed root: $publicBaseUrl"
|
|
|
|
|
|
Write-Host "Velopack feed: $(Join-UrlPath $publicBaseUrl 'releases.win.json')"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
上传命令:
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
.\UploadReleaseToSupabase.ps1 `
|
|
|
|
|
|
-ConfigPath .\Config\update_config.json `
|
|
|
|
|
|
-ReleasePath .\artifacts\releases\my-app
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## 发布后验证
|
|
|
|
|
|
|
|
|
|
|
|
### 1. 验证 feed 可访问
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
$feedUrl = "https://<project-ref>.supabase.co/storage/v1/object/public/app-releases/my-app/windows/stable/releases.win.json"
|
|
|
|
|
|
$feed = Invoke-RestMethod -Uri $feedUrl -Method Get
|
|
|
|
|
|
$feed.Assets | Sort-Object {[version]$_.Version} -Descending | Select-Object -First 5 PackageId,Version,Type,FileName,Size
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
确认:
|
|
|
|
|
|
|
|
|
|
|
|
- 最新版本等于本次发布版本。
|
|
|
|
|
|
- 最新版本至少有 `Full` 资产。
|
|
|
|
|
|
- 非首次发布时应有 `Delta` 资产。
|
|
|
|
|
|
|
|
|
|
|
|
### 2. 验证安装包 URL
|
|
|
|
|
|
|
|
|
|
|
|
```powershell
|
|
|
|
|
|
curl.exe -I "https://<project-ref>.supabase.co/storage/v1/object/public/app-releases/my-app/windows/stable/MyApp-win-Setup.exe"
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
期望结果:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
|
|
HTTP/1.1 200 OK
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 3. 验证客户端更新
|
|
|
|
|
|
|
|
|
|
|
|
建议用两个版本验证:
|
|
|
|
|
|
|
|
|
|
|
|
1. 安装旧版本。
|
|
|
|
|
|
2. 上传新版本发布包。
|
|
|
|
|
|
3. 启动旧版本并触发检查更新。
|
|
|
|
|
|
4. 确认能发现新版本、下载进度正常、重启后版本号变为新版本。
|
|
|
|
|
|
|
|
|
|
|
|
## 复用检查清单
|
|
|
|
|
|
|
|
|
|
|
|
- Supabase Storage 已创建 public bucket。
|
|
|
|
|
|
- 更新包目录规划稳定,不同应用和通道互不覆盖。
|
|
|
|
|
|
- 客户端配置的 `update_server_url` 指向包含 `releases.win.json` 的目录。
|
|
|
|
|
|
- 发布脚本只在受信环境使用 `SUPABASE_SERVICE_ROLE_KEY`。
|
|
|
|
|
|
- 客户端没有保存 Supabase key。
|
|
|
|
|
|
- 每次发布前提高应用版本号。
|
|
|
|
|
|
- `vpk pack` 输出包含 `releases.win.json`、`Setup.exe` 和 `.nupkg`。
|
|
|
|
|
|
- 上传后 `releases.win.json` 可通过公开 URL 访问。
|
|
|
|
|
|
- 安装包 URL 返回 `200 OK`。
|
|
|
|
|
|
- 至少用旧版本安装包做一次真实升级测试。
|
|
|
|
|
|
|
|
|
|
|
|
## 参考文档
|
|
|
|
|
|
|
|
|
|
|
|
- [Velopack C# Getting Started](https://docs.velopack.io/getting-started/csharp)
|
|
|
|
|
|
- [Velopack Integrating Overview](https://docs.velopack.io/integrating/overview)
|
|
|
|
|
|
- [Supabase Storage Buckets](https://supabase.com/docs/guides/storage/buckets/fundamentals)
|
|
|
|
|
|
- [Supabase Creating Buckets](https://supabase.com/docs/guides/storage/buckets/creating-buckets)
|
|
|
|
|
|
- [Supabase Storage Access Control](https://supabase.com/docs/guides/storage/security/access-control)
|
|
|
|
|
|
- [Supabase Standard Uploads](https://supabase.com/docs/guides/storage/uploads/standard-uploads)
|