diff --git a/docs/app-update-system.md b/docs/app-update-system.md index 56acd2e..6e6558a 100644 --- a/docs/app-update-system.md +++ b/docs/app-update-system.md @@ -71,7 +71,7 @@ INSERT INTO app_releases (version, release_date, notes, mandatory) VALUES ('0.2.0', '2026-04-20 10:00:00', '新增:批量导出功能\n优化:性能提升 30%', FALSE); ``` -#### 2.1.2 release_packages - 平台包信息 +#### 2.1.2 mjk_app_release_packages - 平台包信息 | 字段 | 类型 | 说明 | 约束 | |------|------|------|------| @@ -97,7 +97,7 @@ INSERT INTO app_releases (version, release_date, notes, mandatory) VALUES **示例数据**: ```sql -INSERT INTO release_packages (release_id, platform, architecture, filename, file_url, file_size, file_hash) VALUES +INSERT INTO mjk_app_release_packages (release_id, platform, architecture, filename, file_url, file_size, file_hash) VALUES (2, 'darwin', 'x86_64', 'meijiaka_0.1.1_darwin_x86_64.dmg', 'https://cdn.meijiaka.com/releases/meijiaka_0.1.1_darwin_x86_64.dmg', 102400000, 'sha256:abc123...'), @@ -129,7 +129,7 @@ INSERT INTO release_packages (release_id, platform, architecture, filename, file CREATE INDEX idx_releases_release_date ON app_releases(release_date DESC); -- 平台包复合索引 -CREATE INDEX idx_packages_platform_arch ON release_packages(platform, architecture); +CREATE INDEX idx_packages_platform_arch ON mjk_app_release_packages(platform, architecture); -- 下载统计 CREATE INDEX idx_downloads_release_id ON update_downloads(release_id); @@ -151,7 +151,7 @@ CREATE TABLE IF NOT EXISTS app_releases ( ); -- 创建平台包信息表 -CREATE TABLE IF NOT EXISTS release_packages ( +CREATE TABLE IF NOT EXISTS mjk_app_release_packages ( id SERIAL PRIMARY KEY, release_id INTEGER NOT NULL REFERENCES app_releases(id) ON DELETE CASCADE, platform VARCHAR(20) NOT NULL, @@ -177,8 +177,8 @@ CREATE TABLE IF NOT EXISTS update_downloads ( -- 创建索引 CREATE INDEX IF NOT EXISTS idx_releases_version ON app_releases(version); CREATE INDEX IF NOT EXISTS idx_releases_release_date ON app_releases(release_date DESC); -CREATE INDEX IF NOT EXISTS idx_packages_platform_arch ON release_packages(platform, architecture); -CREATE INDEX IF NOT EXISTS idx_packages_release_id ON release_packages(release_id); +CREATE INDEX IF NOT EXISTS idx_packages_platform_arch ON mjk_app_release_packages(platform, architecture); +CREATE INDEX IF NOT EXISTS idx_packages_release_id ON mjk_app_release_packages(release_id); CREATE INDEX IF NOT EXISTS idx_downloads_release_id ON update_downloads(release_id); CREATE INDEX IF NOT EXISTS idx_downloads_download_at ON update_downloads(download_at); @@ -240,7 +240,7 @@ class AppRelease(Base): class ReleasePackage(Base): """平台包信息""" - __tablename__ = "release_packages" + __tablename__ = "mjk_app_release_packages" id: Mapped[int] = mapped_column(Integer, primary_key=True) release_id: Mapped[int] = mapped_column( diff --git a/docs/release-guide.md b/docs/release-guide.md index 6e4a0db..a3fe0af 100644 --- a/docs/release-guide.md +++ b/docs/release-guide.md @@ -170,4 +170,4 @@ Tauri updater 插件已内置跨平台安装逻辑,前端代码无需区分平 | `tauri-app/src-tauri/tauri.conf.json` | updater 配置:公钥 + endpoint URL | | `python-api/scripts/publish_release.py` | 发版脚本(扫描 .sig → 上传七牛云 → 写数据库) | | `python-api/app/api/v1/update.py` | 后端更新检查 API | -| `python-api/app/models/update.py` | 数据库模型(`app_releases` / `release_packages`) | +| `python-api/app/models/update.py` | 数据库模型(`mjk_app_releases` / `mjk_app_release_packages`) | diff --git a/python-api/alembic/versions/8d901bc90e67_rename_mjk_release_packages_to_mjk_app_.py b/python-api/alembic/versions/8d901bc90e67_rename_mjk_release_packages_to_mjk_app_.py new file mode 100644 index 0000000..8673669 --- /dev/null +++ b/python-api/alembic/versions/8d901bc90e67_rename_mjk_release_packages_to_mjk_app_.py @@ -0,0 +1,29 @@ +"""rename mjk_release_packages to mjk_app_release_packages + +Revision ID: 8d901bc90e67 +Revises: 7149f61a2f9c +Create Date: 2026-05-26 10:05:16.921079 + +""" +from typing import Sequence, Union + +from alembic import op + + +# revision identifiers, used by Alembic. +revision: str = '8d901bc90e67' +down_revision: Union[str, Sequence[str], None] = '7149f61a2f9c' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + op.execute("ALTER TABLE IF EXISTS mjk_release_packages RENAME TO mjk_app_release_packages") + op.execute("ALTER INDEX IF EXISTS uix_pkg_platform_arch RENAME TO uix_app_pkg_platform_arch") + + +def downgrade() -> None: + """Downgrade schema.""" + op.execute("ALTER INDEX IF EXISTS uix_app_pkg_platform_arch RENAME TO uix_pkg_platform_arch") + op.execute("ALTER TABLE IF EXISTS mjk_app_release_packages RENAME TO mjk_release_packages") diff --git a/python-api/app/models/update.py b/python-api/app/models/update.py index 0f89127..9e22c13 100644 --- a/python-api/app/models/update.py +++ b/python-api/app/models/update.py @@ -38,7 +38,7 @@ class AppRelease(Base): class ReleasePackage(Base): """平台安装包信息""" - __tablename__ = "mjk_release_packages" + __tablename__ = "mjk_app_release_packages" id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True) release_id: Mapped[int] = mapped_column( @@ -60,5 +60,5 @@ class ReleasePackage(Base): release: Mapped["AppRelease"] = relationship("AppRelease", back_populates="packages") __table_args__ = ( - UniqueConstraint("release_id", "platform", "architecture", name="uix_pkg_platform_arch"), + UniqueConstraint("release_id", "platform", "architecture", name="uix_app_pkg_platform_arch"), ) diff --git a/python-api/scripts/publish_release.py b/python-api/scripts/publish_release.py index b8e64d9..ec48434 100644 --- a/python-api/scripts/publish_release.py +++ b/python-api/scripts/publish_release.py @@ -24,6 +24,9 @@ import sys from pathlib import Path import httpx +from dotenv import load_dotenv + +load_dotenv() def find_packages(bundle_dir: Path) -> list[dict]: @@ -37,21 +40,30 @@ def find_packages(bundle_dir: Path) -> list[dict]: print(f"警告: 签名文件存在但安装包缺失: {pkg_file}") continue - # 解析文件名:{name}_{version}_{target}_{arch}.app.tar.gz - parts = pkg_file.stem.split("_") - # 最后一个部分可能是 arch,需要处理 - arch = "aarch64" if "aarch64" in pkg_file.name or "arm64" in pkg_file.name else "x86_64" - signature = sig_file.read_text().strip() + file_size = pkg_file.stat().st_size + filename = pkg_file.name - packages.append({ - "platform": "darwin", - "architecture": arch, - "filename": pkg_file.name, - "local_path": str(pkg_file), - "signature": signature, - "file_size": pkg_file.stat().st_size, - }) + # 判断文件名是否包含架构标识 + has_arch_marker = any(marker in filename for marker in ["aarch64", "arm64", "x86_64"]) + + if has_arch_marker: + # 文件名含架构标识,按标识解析 + arch = "aarch64" if "aarch64" in filename or "arm64" in filename else "x86_64" + archs = [arch] + else: + # Universal Binary:同时支持 x86_64 和 aarch64 + archs = ["x86_64", "aarch64"] + + for arch in archs: + packages.append({ + "platform": "darwin", + "architecture": arch, + "filename": filename, + "local_path": str(pkg_file), + "signature": signature, + "file_size": file_size, + }) # Windows: .exe + .exe.sig for sig_file in bundle_dir.rglob("*.exe.sig"): @@ -165,16 +177,21 @@ def main(): print(f" - {p['platform']}-{p['architecture']}: {p['filename']} ({p['file_size'] / 1024 / 1024:.1f} MB)") # 2. 上传到七牛云(或构造 URL) + # 按 local_path 去重,同一个文件只上传一次 + uploaded = {} # local_path -> file_url for p in packages: if args.skip_upload: if not args.base_url: print("错误: --skip-upload 时必须提供 --base-url") sys.exit(1) - p["file_url"] = f"{args.base_url.rstrip('/')}/{p['filename']}" + p["file_url"] = f"{args.base_url.rstrip('/')}/{p['platform']}/{p['filename']}" else: - key = f"releases/{args.version}/{p['filename']}" - print(f"上传 {p['filename']} 到七牛云...") - p["file_url"] = upload_to_qiniu(p["local_path"], key) + local_path = p["local_path"] + if local_path not in uploaded: + key = f"meijiaka-zy/releases/{args.version}/{p['platform']}/{p['filename']}" + print(f"上传 {p['filename']} ({p['platform']}) 到七牛云...") + uploaded[local_path] = upload_to_qiniu(local_path, key) + p["file_url"] = uploaded[local_path] # 删除临时字段 del p["local_path"]