メインコンテンツへ移動
·
Arun Babu NeelicattuArun Babu Neelicattu
·19 分で読めます

COPRによるRPMの自動再パッケージング: スタンドアロンバイナリリリースをDNFリポジトリへ橋渡しする

現代のアプリケーションフレームワークではスタンドアロンRPMのビルドは容易ですが、ネイティブなパッケージリポジトリがなければLinuxユーザーは更新のたびに手動でダウンロードして再インストールしなければなりません。本ガイドではFedora COPR、Tito、GitHub Actionsを用いて自動化されたダウンストリームパイプラインを構築します。

再掲載に関する注記: 著者の許可を得て、abn.isの元記事(2026年8月14日公開)から再掲載しています。 本文の文章、見出し、リンク、リスト、コマンド、コード例は、コード内の末尾空白を正規化した以外は変更していません。 Nowledge Labsは要約とタグを編集メタデータとして追加し、元のヒーロー画像をセルフホストし、著者のMermaidソースから両方の図をレンダリングし、元のコールアウトボックスを引用ブロックとして提示し、以下のコミュニティリポジトリに関する通知を追加しました。

コミュニティリポジトリに関する通知: 本記事で説明するCOPRリポジトリはコミュニティによって保守されている非公式のものです。 Nowledge Labsの公式DNFリポジトリではありません。

Tauri、Electron、cargo-packagerといった現代のアプリケーションフレームワークは、デスクトップアプリケーションやCLIバイナリをスタンドアロンの.rpmファイルや.debファイルにビルドすることを容易にします。 しかし、スタンドアロンのパッケージファイルを公開することは、Linuxユーザーにとって特有の配布上の問題を生み出します。 ネイティブなパッケージリポジトリ(dnf / zypper / apt)がなければ、エンドユーザーは更新のたびに手動でパッケージをダウンロードして再インストールしなければなりません。 毎日あるいは1日に複数回の更新をリリースするような動きの速いソフトウェアプロジェクトでは、この手動ワークフローはユーザーにとって持続不可能になります。

新しいアプリケーションが公式のLinuxディストリビューションリポジトリ(FedoraやDebianの公式パッケージリポジトリなど)に受け入れられるには、多大なメンテナの負担が必要です。 提出レビュー、厳格なソースビルドガイドラインへの準拠、依存関係のバンドル解除、そして長い承認待ち行列などです。 小規模チームや独立系開発者が、あらゆるLinuxディストリビューションにわたって公式パッケージを容易に保守することはできません。

本ガイドでは、Fedora COPR、Tito、GitHub Actionsを用いて自動化されたダウンストリームパッケージングパイプラインを構築する方法を詳述します。 このパターンは、アップストリームのスタンドアロンバイナリRPMを取り込み、そのコンポーネントを抽出してモジュール化し、リリースにタグを付け、更新されたCOPRリポジトリを自動的に公開します。

Linuxパッケージングのボトルネック

開発者がフレームワークツールでアプリケーションをパッケージングすると、その出力は多くの場合、GitHub ReleasesやHTTPダウンロードエンドポイントにアップロードされた単一のスタンドアロン.rpmファイルまたは.debファイルです。

アップストリームビルドパイプライン デフォルトのユーザー体験(リポジトリなし) 新リリース公開 アプリコード: Tauri / Electron / Rust ビルドツール: cargo-packager / electron-builder スタンドアロンバイナリ RPM アーティファクト ユーザーがブラウザ経由で手動ダウンロード 手動シェルコマンド: sudo dnf install ./app.rpm 古いローカルインストール

スタンドアロンバイナリリリースのUX上の失敗

  1. システムの自動更新がない: dnf updateやシステム更新GUIアプリケーション(GNOME Software、KDE Discover)は新しいバージョンを検出できません。
  2. 更新の摩擦が大きい: ユーザーはリリースページを再訪し、新しいファイルをダウンロードし、ローカルのインストールコマンドを手動で実行しなければなりません。 リリースの頻度が1日1〜2回に達すると、ユーザーの更新採用率は著しく低下します。
  3. モノリシックな依存関係グラフ: フレームワークのパッケージャは、デスクトップGUI、CLIツール、バックグラウンドのサーバーデーモンを単一の重いパッケージにバンドルすることが多く、サーバー専用やCLI専用のモジュール型デプロイを妨げます。

デスクトップ配布の代替手段: FlatpakとAetherPak

デスクトップアプリケーションについては、Flatpakが自動更新を備えたサンドボックス化された配布メカニズムを提供します。 しかし、Flathubへのアプリケーション提出には厳格な提出要件と手動のレビュアー承認が伴います。

セルフマネージドFlatpakリポジトリ: AetherPak

Flathub以外で自動化されたFlatpak配布を求める開発者向けに、プロジェクトはAetherPakのような代替インデックスを通じて公開できます。

具体的なリファレンス実装としてLemonade Tauri App Flatpakリポジトリがあり、これはTauriデスクトップアプリケーションをスタンドアロンのFlatpakバンドルにパッケージングし、アップストリームのリリースが現れると自動更新をサポートします。

サーバーデーモン、CLIバイナリ、systemd統合については、ネイティブRPMリポジトリがFedora、RHEL、CentOS Stream、openSUSEエコシステムにおける標準的なアプローチであり続けています。

自動再パッケージングパイプラインのアーキテクチャ

Fedora COPR(元はCool Other Package Repositories、現在はCommunity Projects)は、メンテナやコミュニティコントリビューターが自動RPMリポジトリをホストすることを可能にします。 COPRをTito(Git内でRPMパッケージのライフサイクルを管理するツール)およびGitHub Actionsと組み合わせることで、スタンドアロンバイナリRPMのダウンロードを完全に自動化されたリポジトリパイプラインへと変換できます。

COPR Dist-Gitとソースアセットの管理

コンパイル済みバイナリを再パッケージングする際のよくある誤解は、大きなバイナリファイル(x86_64-unknown-linux-gnu.rpm)をGitに直接コミットしたり、Git LFSで追跡したりしなければならないというものです。 Fedora COPRでは、大きなバイナリソースファイルはGit履歴に属しません。

代わりに、Fedora COPRはdist-gitルックアサイドキャッシュのメカニズムを活用します。

  • Gitリポジトリは軽量なテキストファイルのみを追跡します。 すなわち.specファイル、設定メタデータ、そしてバイナリペイロードファイルのMD5/SHA256チェックサムを含むsourcesファイルです。
  • ビルド実行中、COPRのdist-git-client sourcesツールは、分離されたMockビルドchroot内でrpmbuildを起動する前に、ルックアサイドキャッシュまたは指定されたHTTPエンドポイントから対応するバイナリアーティファクトを取得します。
  • Mockビルド実行中に動的ダウンロードが発生する場合は、COPRプロジェクト設定でenable_net: True(またはnet_access: true)が構成されていることを確認してください。 対応する設定は「Allow internet access」チェックボックスです。
alt [新しいバージョンを検出] バージョンチェックとバイナリ RPM のダウンロードを実行 最新のダウンロード URL / ヘッダーを照会 バージョンメタデータを返す(例: 0.10.59) バイナリ RPM アーティファクトをダウンロード(x86_64-unknown-linux-gnu.rpm) バージョンを更新: spec ファイル内の tag と reset Release: tito tag --use-version <version> を実行 変更をコミットし、チェンジログと Git タグを生成 コミットとタグを GitHub にプッシュ copr-cli 経由で SRPM ビルドをトリガー lookaside ソースを取得し、RPM ペイロードを展開してサブパッケージをビルド dnf update(新しいリリースを自動的に取得) GitHub Actions(4時間 Cron) アップストリーム HTTP エンドポイント update_version.py Tito リリースマネージャー Fedora COPR サービス エンドユーザー(DNF)

設計根拠(COPR Dist-Gitルックアサイドキャッシュvs Git LFS)

コンパイル済みバイナリRPMをGitリポジトリに直接コミットすると、リポジトリが急速に肥大化します。 COPRのdist-gitルックアサイドキャッシュを利用することで、パッケージング用Gitリポジトリを軽量に保つことができます(テキストのspecファイルとバージョンスクリプトのみを含む)。 Git LFSは、外部のルックアサイドストレージが利用できないセルフホスト型またはエアギャップ型のCI環境における代替手段であり続けます。

設計根拠(rpm2cpioによる再パッケージングvsソースコンパイル)

プロプライエタリまたは複雑なマルチアーキテクチャバイナリを標準的なRPMビルド環境内でソースから再ビルドするには、多くの場合、複雑なツールチェーンを再現する必要があります。 コンパイル済みのアップストリームバイナリRPMをspecの%prepフェーズでrpm2cpioを介して再パッケージングすることで、配布メカニズムをビルド環境から分離しつつ、ネイティブなシステム統合(systemdサービスユニット、デスクトップメニューエントリキャッシュ、バイナリシンボリックリンク)を維持できます。

実装ウォークスルー

1. RPM Specファイルの設計

specファイルは、事前に取得されたアップストリームバイナリ.rpmファイルを受け取り、rpm2cpioとcpioを用いてその内容を展開し、標準的なRPMマクロ(%{_bindir}、%{_datadir}、%{_prefix}/lib)を用いてファイルを適切なパッケージパスとサブパッケージに配分します。

以下は、4つのサブパッケージをサポートするspecファイル(nowledge-mem.spec)の例です。 メタパッケージ(nowledge-mem)、CLIパッケージ(nowledge-mem-cli)、GUIパッケージ(nowledge-mem-desktop)、ヘッドレスデーモンパッケージ(nowledge-mem-server)です。

spec
Name: nowledge-mem Version: 0.10.59 Release: 1%{?dist} Summary: Personal memory and context management system (Metapackage) License: LicenseRef-Proprietary URL: https://download-mem.nowledge.co Source0: x86_64-unknown-linux-gnu.rpm ExclusiveArch: x86_64 # AutoReqProv: no prevents RPM build helpers from failing on unbundled internal libraries in pre-compiled binaries AutoReqProv: no %global debug_package %{nil} BuildRequires: cpio BuildRequires: rpm BuildRequires: systemd-rpm-macros Provides: nmem = %{version}-%{release} Requires: nowledge-mem-cli = %{version}-%{release} Requires: nowledge-mem-desktop = %{version}-%{release} Requires: nowledge-mem-server = %{version}-%{release} %description Metapackage installing desktop GUI, CLI tools, and local backend server. %package cli Summary: Nowledge Mem CLI and TUI tools AutoReqProv: no %description cli Command-line interface (nmem) and Terminal UI (nmem-tui). %package desktop Summary: Nowledge Mem Desktop GUI client AutoReqProv: no Requires: nowledge-mem-cli = %{version}-%{release} Requires: gtk3 Requires: (webkit2gtk4.1 or webkit2gtk4.0 or webkit2gtk3) %description desktop Desktop GUI client and desktop entry icons. %package server Summary: Nowledge Mem backend server daemon AutoReqProv: no %{?systemd_requires} %description server Headless server daemon with systemd user service units. %prep %setup -q -c -T # Multi-path search for binary payload across local directory, Mock chroot, and SRPM sources RPM_FILE="" for f in \ "/sources/build/x86_64-unknown-linux-gnu.rpm" \ "/sources/x86_64-unknown-linux-gnu.rpm" \ "/builddir/build/SOURCES/x86_64-unknown-linux-gnu.rpm" \ "%{_sourcedir}/x86_64-unknown-linux-gnu.rpm" \ "%{SOURCE0}"; do if [ -f "$f" ] && rpm2cpio "$f" >/dev/null 2>&1; then RPM_FILE="$f" break fi done if [ -n "$RPM_FILE" ]; then echo "Extracting binary payload from: $RPM_FILE" rpm2cpio "$RPM_FILE" | cpio -idmv else echo "ERROR: Upstream binary RPM not found in build paths." >&2 exit 1 fi %build # Pre-compiled binary package; no build step required. %install rm -rf %{buildroot} mkdir -p %{buildroot}%{_bindir} mkdir -p %{buildroot}%{_prefix}/lib mkdir -p %{buildroot}%{_datadir}/applications mkdir -p %{buildroot}%{_datadir}/icons mkdir -p %{buildroot}%{_userunitdir} cp -a usr/bin/* %{buildroot}%{_bindir}/ 2>/dev/null || true cp -a "usr/lib/Nowledge Mem" %{buildroot}%{_prefix}/lib/ cp -a usr/share/applications/* %{buildroot}%{_datadir}/applications/ 2>/dev/null || true cp -a usr/share/icons/* %{buildroot}%{_datadir}/icons/ 2>/dev/null || true # Install Systemd User Service Unit cat << 'EOF' > %{buildroot}%{_userunitdir}/nowledge-mem.service [Unit] Description=Nowledge Mem Server Daemon (User Service) After=network.target [Service] Type=simple ExecStart=/usr/bin/nmem-server Restart=always RestartSec=5 [Install] WantedBy=default.target EOF %post desktop if command -v gtk-update-icon-cache >/dev/null 2>&1; then gtk-update-icon-cache -q -t -f %{_datadir}/icons/hicolor 2>/dev/null || true fi %post server %systemd_user_post nowledge-mem.service %preun server %systemd_user_preun nowledge-mem.service %files cli %{_bindir}/nmem "%{_prefix}/lib/Nowledge Mem/_up_/rust-backend/nmem" %files desktop %{_bindir}/nowledge-mem "%{_datadir}/applications/Nowledge Mem.desktop" %{_datadir}/icons/hicolor/* %files server %{_bindir}/nmem-server %{_userunitdir}/nowledge-mem.service "%{_prefix}/lib/Nowledge Mem/_up_/rust-backend/nmem-server"

設計根拠(サブパッケージのモジュール化)

モノリシックなアプリケーションを個別のサブパッケージ(cli、desktop、server)に分割することで、ヘッドレスサーバーやリモートコンテナ環境は、gtk3やwebkit2gtkのような重いデスクトップ依存関係を引き込むことなくnowledge-mem-serverをインストールできます。

2. アップストリームバージョンの解決とダウンローダースクリプト

バージョンチェックを自動化し、Tito SRPMビルド用のバイナリペイロードを事前取得するには、Pythonスクリプト(update_version.py)を書いて、アップストリームのリダイレクトURLを検査し、specファイルのVersion:を更新し、Release:を1にリセットし、バイナリアーティファクトをx86_64-unknown-linux-gnu.rpmに保存します。

python
#!/usr/bin/env python3 """ Upstream Version Resolver and Downloader for Repackaging Pipelines. Note: Adjust regex patterns in resolve_latest_version() to match your project's specific upstream release endpoint or redirect URL structure. """ import argparse import os import re import urllib.request import sys from pathlib import Path VERSION_REDIRECT_URL = "https://download-mem.nowledge.co/download-mem-rpm" SPEC_FILE_PATH = Path("nowledge-mem.spec") BINARY_OUTPUT_PATH = Path("x86_64-unknown-linux-gnu.rpm") def resolve_latest_version(): req = urllib.request.Request( VERSION_REDIRECT_URL, headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64)"} ) with urllib.request.urlopen(req) as resp: final_url = resp.geturl() # Adapt regex pattern to match upstream URL scheme (e.g., /app/<version>/ or /releases/v<version>/) match = re.search(r"/app/([^/]+)/", final_url) if not match: raise RuntimeError(f"Could not parse version from URL: {final_url}") return match.group(1), final_url def update_spec_version(new_version, is_new=True): content = SPEC_FILE_PATH.read_text() updated = re.sub(r"^(Version:\s*)[^\s]+", f"\\g<1>{new_version}", content, flags=re.MULTILINE) if is_new: updated = re.sub(r"^(Release:\s*)[^\s]+", r"\g<1>1%{?dist}", updated, flags=re.MULTILINE) if content != updated: SPEC_FILE_PATH.write_text(updated) def download_upstream_rpm(download_url): print(f"Downloading upstream binary artifact from {download_url}...") req = urllib.request.Request( download_url, headers={"User-Agent": "Mozilla/5.0 (X11; Linux x86_64)"} ) with urllib.request.urlopen(req) as resp, open(BINARY_OUTPUT_PATH, "wb") as f: while chunk := resp.read(8192): f.write(chunk) print(f"Saved binary payload to {BINARY_OUTPUT_PATH}") def main(): parser = argparse.ArgumentParser() parser.add_argument("--check-only", action="store_true") args = parser.parse_args() latest_version, download_url = resolve_latest_version() current_version = None if SPEC_FILE_PATH.exists(): match = re.search(r"^Version:\s*([^\s]+)", SPEC_FILE_PATH.read_text(), re.MULTILINE) if match: current_version = match.group(1) is_new = (current_version != latest_version) if args.check_only: print(f"is_new={'true' if is_new else 'false'}") sys.exit(0) if is_new or not BINARY_OUTPUT_PATH.exists(): update_spec_version(latest_version, is_new=is_new) download_upstream_rpm(download_url) if __name__ == "__main__": main()

3. Titoによる自動バージョンタグ付け

Titoは、パッケージバージョンの維持、チェンジログの生成、Gitタグの自動管理を行うことで、GitベースのRPM管理を簡素化します。

Git衛生ルール

Titoを使用する際は、specファイル内の%changelogを手動で編集することは避けてください。 Titoは明示的に指定されない限り、tito tagの間に%changelogを管理します。 作業ツリー内のコミットされていない手動変更は、tito tagを失敗させます。

プロジェクトのルートディレクトリでTitoを初期化します。

bash
tito init

specファイルが更新されたときに新しいリリースタグを自動的に作成するには、次のようにします。

bash
tito tag --use-version "0.10.59" --accept-auto-changelog

4. 継続的インテグレーションワークフロー(GitHub Actions)

GitHub Actionsワークフロー(.github/workflows/check-updates.yml)を構成して、アップストリームエンドポイントを4時間ごとにポーリングします。 新しいバージョンが検出されると、tito tagがクリーンなGitリリースタグを生成し、SRPMの生成とcopr-cliはdist-gitソース仕様を活用します。

yaml
name: Automated Update Check & Tito Release on: schedule: - cron: '0 */4 * * *' workflow_dispatch: permissions: contents: write jobs: check-and-release: runs-on: ubuntu-latest container: image: fedora:latest options: --user root steps: - name: Install Dependencies run: | dnf install -y git tito python3 copr-cli rpmdevtools git config --global user.name "github-actions[bot]" git config --global user.email "github-actions[bot]@users.noreply.github.com" git config --global --add safe.directory "*" - name: Checkout Code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Check Upstream Version and Update Spec id: updater run: | python3 update_version.py - name: Tag Release with Tito id: tito_release run: | if [ -n "$(git status --porcelain nowledge-mem.spec)" ]; then NEW_VER=$(python3 -c "import re; print(re.search(r'^Version:\s*([^\s]+)', open('nowledge-mem.spec').read(), re.MULTILINE).group(1))") # Tito automatically stages modified spec files, updates changelog, and creates release commit tito tag --use-version "${NEW_VER}" --accept-auto-changelog echo "CHANGED=true" >> $GITHUB_OUTPUT fi - name: Push Commits and Tags if: steps.tito_release.outputs.CHANGED == 'true' run: | git push origin main --follow-tags - name: Dispatch Build to COPR if: steps.tito_release.outputs.CHANGED == 'true' env: COPR_CONFIG: ${{ secrets.COPR_CONFIG }} run: | mkdir -p ~/.config echo "$COPR_CONFIG" > ~/.config/copr tito build --srpm --output=build/ SRPM_FILE=$(ls build/*.src.rpm | head -n 1) copr-cli build user/nowledge-mem "$SRPM_FILE" --nowait

設計根拠(CIにおける自動Titoタグ付け)

コンテナ化されたGitHub Actionsランナー内でtito tagを自動化することで、一貫したリリースバージョニングとチェンジログエントリを確保しつつ、ローカルの開発者セットアップを最小限に保つことができます。

セキュリティモデルと信頼に関する注意事項

サードパーティのバイナリアーティファクトを再パッケージングすることは、リポジトリのメンテナとエンドユーザーの双方が認識しなければならないセキュリティ上の責任を伴います。

セキュリティに関する注意事項(サードパーティリポジトリとコンパイル済みバイナリ)

再パッケージングされたバイナリRPMを含むコミュニティCOPRリポジトリを有効にする場合、エンドユーザーはアップストリームプロジェクトのメンテナと再パッケージングリポジトリのメンテナの両方に完全な信頼を委ねることになります。

  • ソースビルドの検証なし: 再パッケージングパイプラインはコンパイル済みバイナリ(rpm2cpio)を抽出します。 COPRビルダーはソースコードをコンパイルしないため、COPRビルドログは抽出とパッケージングの手順のみを検証し、バイナリのコンパイルの完全性は検証しません。
  • リポジトリの分離: ユーザーは信頼できないコミュニティCOPRリポジトリを本番マシンに追加することは避けるべきです。 sudo dnf copr enable <user>/<repo>を実行する前に、必ず基盤となるspecファイルとGitHub Actionsパイプラインを検査してください。

コマンドリファレンスのまとめ

このパイプラインを通じて公開されたパッケージを利用するユーザー向け:

bash
# Enable the community COPR repository sudo dnf copr enable user/nowledge-mem # Install the full metapackage (Desktop + Server + CLI) sudo dnf install nowledge-mem # Or install headless server daemon only sudo dnf install nowledge-mem-server # Enable and start the systemd user service systemctl --user enable --now nowledge-mem.service

この自動化が整えば、プロジェクトのメンテナは標準的なフレームワークビルドツールでスタンドアロンバイナリを公開でき、Linuxユーザーはネイティブなdnf update機能とsystemd統合を維持できます。

プロジェクトのソースコード

この取り組みのソースコードはGitHubで入手できます。

© 2026 Nowledge Labs.ナレッジレイヤーを構築する。