diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..481be50 --- /dev/null +++ b/flake.nix @@ -0,0 +1,40 @@ +{ + description = "MeloASR — web-backed voice input for Fcitx5"; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = { self, nixpkgs }: + let + systems = [ "x86_64-linux" "aarch64-linux" ]; + forAllSystems = nixpkgs.lib.genAttrs systems; + in { + packages = forAllSystems (system: + let pkgs = import nixpkgs { inherit system; }; + in { + default = pkgs.callPackage ./packaging/nix/package.nix { }; + meloasr = self.packages.${system}.default; + }); + + apps = forAllSystems (system: { + default = { + type = "app"; + program = "${self.packages.${system}.default}/bin/meloasr"; + }; + }); + + devShells = forAllSystems (system: + let pkgs = import nixpkgs { inherit system; }; + in { + default = pkgs.mkShell { + packages = with pkgs; [ + nodejs + cmake + pkg-config + fcitx5 + nlohmann_json + ]; + }; + }); + }; +} + diff --git a/packaging/README.md b/packaging/README.md new file mode 100644 index 0000000..143dc58 --- /dev/null +++ b/packaging/README.md @@ -0,0 +1,75 @@ +# Linux 分发打包 + +MeloASR 0.1.0 的系统包由两部分组成: + +- Electron 应用安装到 `/opt/meloasr`,命令入口为 `/usr/bin/meloasr`; +- Fcitx5 常驻 addon 由目标发行版中的 CMake/Fcitx5 开发包编译,并安装到该环境报告的 addon 目录。 + +安装包同时提供桌面入口、项目图标和 `/etc/xdg/autostart/meloasr.desktop`,因此安装后的默认行为是随桌面会话启动。设置页关闭自启动时,应用需要在用户级 autostart 目录写入覆盖项,不能删除系统包所属文件。 + +## 统一构建契约 + +deb、rpm 和 Arch 配置期望项目提供: + +```bash +npm run build:linux:dir +``` + +该命令应在 `release/linux-unpacked/` 生成可直接运行的 Electron 目录,并且主程序名必须是 `meloasr`。仓库提供的 `electron-builder.yml` 只生成这一中间目录;deb/rpm/Arch 的系统文件仍由各自脚本管理,避免用安装钩子复制未被包管理器追踪的 Fcitx5 文件。 + +主项目可将脚本定义为: + +```json +{ + "build:linux:dir": "npm run build && electron-builder --config packaging/electron-builder.yml --linux dir" +} +``` + +手工检查安装树: + +```bash +packaging/scripts/stage-linux.sh release/linux-unpacked packaging/out/root +``` + +## deb + +在 Debian/Ubuntu 对应目标环境中安装 Node.js 构建依赖、CMake、C++ 编译器、Fcitx5 开发包与 nlohmann-json 开发包,然后执行: + +```bash +npm run build:linux:dir +packaging/deb/build-deb.sh +``` + +产物位于 `packaging/out/meloasr_0.1.0_.deb`。依赖名称按 Debian/Ubuntu 系列填写,但尚未在各发行版完成安装验证。 + +## rpm + +在目标 RPM 发行版中准备相同的编译依赖和 `rpmbuild`,然后执行: + +```bash +npm run build:linux:dir +packaging/rpm/build-rpm.sh +``` + +产物复制到 `packaging/out/`。RPM 发行版之间的 Fcitx5 开发包和运行库名称可能不同,正式发布前需要分别在 Fedora/openSUSE 等目标环境验证并维护发行版 spec。 + +## Arch/pacman + +参见 [`arch/README.md`](arch/README.md)。PKGBUILD 在干净 chroot 中从源码构建 Electron 应用和 Fcitx5 addon。 + +## Nix flake + +```bash +nix build +nix run +``` + +flake 使用 nixpkgs 的 Electron,Node 依赖由 `package-lock.json` 的 integrity 数据导入,不需要手写固定输出 hash。仓库首次确定发布地址后,需要锁定并提交 `flake.lock`。当前环境没有 Nix,尚未实际执行上述命令。 + +## 发布前必须完成 + +- 将所有 `example.invalid` 替换成正式仓库地址; +- 明确项目许可证,并替换临时的 `LicenseRef-Proprietary`/Nix license; +- 生成 Arch 源码归档真实 SHA-256; +- 在干净的 Debian、RPM、Arch 和 Nix 构建环境逐一构建、安装、卸载; +- 验证 Fcitx5 能发现 addon,桌面菜单/托盘图标、自启动开关和包升级均正常。 diff --git a/packaging/arch/PKGBUILD b/packaging/arch/PKGBUILD new file mode 100644 index 0000000..f1795cf --- /dev/null +++ b/packaging/arch/PKGBUILD @@ -0,0 +1,45 @@ +pkgname=meloasr +pkgver=0.1.0 +pkgrel=1 +pkgdesc='Web-backed voice input for Fcitx5' +arch=('x86_64') +url='https://example.invalid/meloasr' +license=('custom') +depends=('fcitx5' 'gtk3' 'nss' 'libxss' 'mesa' 'alsa-lib') +makedepends=('nodejs' 'npm' 'cmake' 'gcc' 'nlohmann-json') +source=("${pkgname}-${pkgver}.tar.gz") +sha256sums=('SKIP') + +build() { + cd "${srcdir}/${pkgname}-${pkgver}" + npm ci + npm run build:linux:dir + cmake -S linux/fcitx5 -B build/fcitx5 \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr + cmake --build build/fcitx5 --parallel +} + +check() { + cd "${srcdir}/${pkgname}-${pkgver}" + npm test + ctest --test-dir build/fcitx5 --output-on-failure +} + +package() { + cd "${srcdir}/${pkgname}-${pkgver}" + + install -d "${pkgdir}/opt/meloasr" + cp -a release/linux-unpacked/. "${pkgdir}/opt/meloasr/" + install -Dm755 packaging/assets/meloasr-wrapper "${pkgdir}/usr/bin/meloasr" + install -Dm644 packaging/assets/meloasr.desktop \ + "${pkgdir}/usr/share/applications/meloasr.desktop" + install -Dm644 packaging/assets/meloasr-autostart.desktop \ + "${pkgdir}/etc/xdg/autostart/meloasr.desktop" + install -Dm644 packaging/assets/meloasr.metainfo.xml \ + "${pkgdir}/usr/share/metainfo/meloasr.metainfo.xml" + install -Dm644 logo.png \ + "${pkgdir}/usr/share/pixmaps/meloasr.png" + + DESTDIR="${pkgdir}" cmake --install build/fcitx5 +} diff --git a/packaging/arch/README.md b/packaging/arch/README.md new file mode 100644 index 0000000..42de696 --- /dev/null +++ b/packaging/arch/README.md @@ -0,0 +1,10 @@ +# Arch Linux 构建 + +发布时把项目源码归档为 `meloasr-0.1.0.tar.gz`,与 `PKGBUILD` 放在同一目录,随后运行: + +```bash +makepkg --syncdeps --cleanbuild +``` + +正式发布必须把 `source` 改为仓库的不可变 release URL,并以 `updpkgsums` 写入真实 SHA-256;仓库地址确定前保留的 `example.invalid` 和 `SKIP` 不能用于发布。 + diff --git a/packaging/assets/meloasr-autostart.desktop b/packaging/assets/meloasr-autostart.desktop new file mode 100644 index 0000000..8752ab9 --- /dev/null +++ b/packaging/assets/meloasr-autostart.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Type=Application +Name=MeloASR +Comment=Start MeloASR when signing in +Comment[zh_CN]=登录桌面后启动 MeloASR +Exec=meloasr --autostart +Icon=meloasr +Terminal=false +X-GNOME-Autostart-enabled=true +X-KDE-autostart-after=panel +NoDisplay=true + diff --git a/packaging/assets/meloasr-wrapper b/packaging/assets/meloasr-wrapper new file mode 100755 index 0000000..d23f255 --- /dev/null +++ b/packaging/assets/meloasr-wrapper @@ -0,0 +1,3 @@ +#!/bin/sh +exec /opt/meloasr/meloasr "$@" + diff --git a/packaging/assets/meloasr.desktop b/packaging/assets/meloasr.desktop new file mode 100644 index 0000000..0b5d8b5 --- /dev/null +++ b/packaging/assets/meloasr.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Application +Name=MeloASR +Comment=Web-backed voice input for Fcitx5 +Comment[zh_CN]=基于网页后端的 Fcitx5 语音输入工具 +Exec=meloasr +Icon=meloasr +Terminal=false +Categories=Utility;Accessibility; +StartupNotify=false diff --git a/packaging/assets/meloasr.metainfo.xml b/packaging/assets/meloasr.metainfo.xml new file mode 100644 index 0000000..cdb4c1f --- /dev/null +++ b/packaging/assets/meloasr.metainfo.xml @@ -0,0 +1,21 @@ + + + io.github.meloasr.MeloASR + MeloASR + Web-backed voice input for Fcitx5 + 基于网页后端的 Fcitx5 语音输入工具 + CC0-1.0 + LicenseRef-proprietary + +

MeloASR connects supported web speech-recognition backends to the active Fcitx5 input context.

+

MeloASR 将支持的网页语音识别后端连接到当前 Fcitx5 输入上下文。

+
+ https://example.invalid/meloasr + meloasr.desktop + + meloasr + + + + +
diff --git a/packaging/deb/build-deb.sh b/packaging/deb/build-deb.sh new file mode 100755 index 0000000..1d67ffd --- /dev/null +++ b/packaging/deb/build-deb.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail + +project_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" +version="${MELOASR_VERSION:-0.1.0}" +arch="${MELOASR_DEB_ARCH:-$(dpkg --print-architecture)}" +app_dir="${1:-${project_dir}/release/linux-unpacked}" +work_dir="${project_dir}/packaging/out/deb-root" +output_dir="${project_dir}/packaging/out" + +rm -rf -- "${work_dir}" +"${project_dir}/packaging/scripts/stage-linux.sh" "${app_dir}" "${work_dir}" + +install -d "${work_dir}/DEBIAN" +installed_size="$(du -sk "${work_dir}" | cut -f1)" +sed \ + -e "s/@VERSION@/${version}/g" \ + -e "s/@ARCH@/${arch}/g" \ + -e "s/@INSTALLED_SIZE@/${installed_size}/g" \ + "${project_dir}/packaging/deb/control.in" > "${work_dir}/DEBIAN/control" + +mkdir -p "${output_dir}" +dpkg-deb --root-owner-group --build "${work_dir}" \ + "${output_dir}/meloasr_${version}_${arch}.deb" diff --git a/packaging/deb/control.in b/packaging/deb/control.in new file mode 100644 index 0000000..13fa7a9 --- /dev/null +++ b/packaging/deb/control.in @@ -0,0 +1,11 @@ +Package: meloasr +Version: @VERSION@ +Section: sound +Priority: optional +Architecture: @ARCH@ +Maintainer: MeloASR contributors +Installed-Size: @INSTALLED_SIZE@ +Depends: fcitx5, libc6, libstdc++6, libgtk-3-0, libnss3, libxss1, libgbm1, libasound2 | libasound2t64 +Description: Web-backed voice input for Fcitx5 + MeloASR hosts supported web speech-recognition backends and sends their + live corrected text to the active Fcitx5 input context. diff --git a/packaging/electron-builder.yml b/packaging/electron-builder.yml new file mode 100644 index 0000000..f451665 --- /dev/null +++ b/packaging/electron-builder.yml @@ -0,0 +1,22 @@ +appId: io.github.meloasr.MeloASR +productName: MeloASR +asar: true + +directories: + output: release + +files: + - dist/**/* + - package.json + +extraResources: + - from: logo.png + to: logo.png + +linux: + target: + - dir + executableName: meloasr + icon: logo.png + category: Utility + diff --git a/packaging/nix/package.nix b/packaging/nix/package.nix new file mode 100644 index 0000000..406724b --- /dev/null +++ b/packaging/nix/package.nix @@ -0,0 +1,90 @@ +{ lib +, stdenv +, importNpmLock +, nodejs +, electron +, cmake +, pkg-config +, makeWrapper +, fcitx5 +, nlohmann_json +}: + +stdenv.mkDerivation (finalAttrs: { + pname = "meloasr"; + version = "0.1.0"; + + src = ../..; + + npmDeps = importNpmLock { + npmRoot = finalAttrs.src; + }; + + nativeBuildInputs = [ + nodejs + importNpmLock.npmConfigHook + cmake + pkg-config + makeWrapper + ]; + + buildInputs = [ fcitx5 nlohmann_json ]; + + buildPhase = '' + runHook preBuild + npm run build + cmake -S linux/fcitx5 -B build/fcitx5 \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="$out" \ + -DFCITX_INSTALL_USE_FCITX_SYS_PATHS=OFF + cmake --build build/fcitx5 --parallel "$NIX_BUILD_CORES" + runHook postBuild + ''; + + checkPhase = '' + runHook preCheck + npm test + ctest --test-dir build/fcitx5 --output-on-failure + runHook postCheck + ''; + doCheck = true; + + installPhase = '' + runHook preInstall + + appDir="$out/share/meloasr/app" + mkdir -p "$appDir" "$out/bin" "$out/share/applications" \ + "$out/share/pixmaps" "$out/share/metainfo" \ + "$out/etc/xdg/autostart" + + cp package.json "$appDir/" + cp -r dist "$appDir/" + if [ -d node_modules ]; then + npm prune --omit=dev --ignore-scripts + cp -r node_modules "$appDir/" + fi + + install -Dm644 packaging/assets/meloasr.desktop \ + "$out/share/applications/meloasr.desktop" + install -Dm644 packaging/assets/meloasr-autostart.desktop \ + "$out/etc/xdg/autostart/meloasr.desktop" + install -Dm644 packaging/assets/meloasr.metainfo.xml \ + "$out/share/metainfo/meloasr.metainfo.xml" + install -Dm644 logo.png \ + "$out/share/pixmaps/meloasr.png" + + cmake --install build/fcitx5 + makeWrapper ${electron}/bin/electron "$out/bin/meloasr" \ + --add-flags "$appDir" + + runHook postInstall + ''; + + meta = { + description = "Web-backed voice input for Fcitx5"; + homepage = "https://example.invalid/meloasr"; + license = lib.licenses.unfreeRedistributable; + mainProgram = "meloasr"; + platforms = lib.platforms.linux; + }; +}) diff --git a/packaging/rpm/build-rpm.sh b/packaging/rpm/build-rpm.sh new file mode 100755 index 0000000..d7e4fe5 --- /dev/null +++ b/packaging/rpm/build-rpm.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -euo pipefail + +project_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" +version="${MELOASR_VERSION:-0.1.0}" +app_dir="${1:-${project_dir}/release/linux-unpacked}" +stage_dir="${project_dir}/packaging/out/rpm-root" +top_dir="${project_dir}/packaging/out/rpmbuild" + +command -v rpmbuild >/dev/null || { + echo "未找到 rpmbuild,请先安装 RPM 构建工具。" >&2 + exit 1 +} + +rm -rf -- "${stage_dir}" "${top_dir}" +"${project_dir}/packaging/scripts/stage-linux.sh" "${app_dir}" "${stage_dir}" +mkdir -p "${top_dir}"/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} +tar -C "${stage_dir}" -czf "${top_dir}/SOURCES/meloasr-root.tar.gz" . + +rpmbuild -bb \ + --define "_topdir ${top_dir}" \ + --define "meloasr_version ${version}" \ + "${project_dir}/packaging/rpm/meloasr-binary.spec" + +find "${top_dir}/RPMS" -type f -name '*.rpm' -exec cp -f {} "${project_dir}/packaging/out/" \; + diff --git a/packaging/rpm/meloasr-binary.spec b/packaging/rpm/meloasr-binary.spec new file mode 100644 index 0000000..afbfaf1 --- /dev/null +++ b/packaging/rpm/meloasr-binary.spec @@ -0,0 +1,40 @@ +Name: meloasr +Version: %{meloasr_version} +Release: 1%{?dist} +Summary: Web-backed voice input for Fcitx5 +License: LicenseRef-Proprietary +URL: https://example.invalid/meloasr +Source0: meloasr-root.tar.gz + +Requires: fcitx5 +Requires: gtk3 +Requires: nss +Requires: libXScrnSaver +Requires: mesa-libgbm + +%description +MeloASR hosts supported web speech-recognition backends and sends their live +corrected text to the active Fcitx5 input context. + +%prep + +%build + +%install +mkdir -p %{buildroot} +tar -C %{buildroot} -xzf %{SOURCE0} + +%files +%defattr(-,root,root,-) +/opt/meloasr +/usr/bin/meloasr +/usr/lib*/fcitx5/libmeloasr.so +/usr/share/applications/meloasr.desktop +/usr/share/pixmaps/meloasr.png +/usr/share/metainfo/meloasr.metainfo.xml +/usr/share/fcitx5/addon/meloasr.conf +/etc/xdg/autostart/meloasr.desktop + +%changelog +* Sun Aug 23 2026 MeloASR contributors - 0.1.0-1 +- Initial package diff --git a/packaging/scripts/stage-linux.sh b/packaging/scripts/stage-linux.sh new file mode 100755 index 0000000..a2f8092 --- /dev/null +++ b/packaging/scripts/stage-linux.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -euo pipefail + +project_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" +app_dir="${1:-${project_dir}/release/linux-unpacked}" +stage_dir="${2:-${project_dir}/packaging/out/root}" +addon_build_dir="${MELOASR_ADDON_BUILD_DIR:-${project_dir}/packaging/out/fcitx5-build}" + +if [[ ! -d "${app_dir}" ]]; then + echo "缺少 Electron 解包目录:${app_dir}" >&2 + echo "请先运行项目提供的 Linux directory 构建脚本。" >&2 + exit 1 +fi + +if [[ ! -x "${app_dir}/meloasr" ]]; then + echo "Electron 解包目录中没有可执行文件 meloasr:${app_dir}" >&2 + exit 1 +fi + +install -d \ + "${stage_dir}/opt/meloasr" \ + "${stage_dir}/usr/bin" \ + "${stage_dir}/usr/share/applications" \ + "${stage_dir}/usr/share/pixmaps" \ + "${stage_dir}/usr/share/metainfo" \ + "${stage_dir}/etc/xdg/autostart" + +cp -a "${app_dir}/." "${stage_dir}/opt/meloasr/" +install -Dm755 "${project_dir}/packaging/assets/meloasr-wrapper" \ + "${stage_dir}/usr/bin/meloasr" +install -Dm644 "${project_dir}/packaging/assets/meloasr.desktop" \ + "${stage_dir}/usr/share/applications/meloasr.desktop" +install -Dm644 "${project_dir}/packaging/assets/meloasr-autostart.desktop" \ + "${stage_dir}/etc/xdg/autostart/meloasr.desktop" +install -Dm644 "${project_dir}/packaging/assets/meloasr.metainfo.xml" \ + "${stage_dir}/usr/share/metainfo/meloasr.metainfo.xml" +install -Dm644 "${project_dir}/logo.png" \ + "${stage_dir}/usr/share/pixmaps/meloasr.png" + +cmake -S "${project_dir}/linux/fcitx5" -B "${addon_build_dir}" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/usr +cmake --build "${addon_build_dir}" --parallel +DESTDIR="${stage_dir}" cmake --install "${addon_build_dir}" + +echo "已生成安装根目录:${stage_dir}"