name: Build # main のコミットに打った v*.* タグ(例: v0.1 / v0.1.0)の push でトリガー。 # タグはリリース時にだけ打つので、実質「リリースビルド」として働く。 # 通常のコミットでは走らない — 3 OS 分の Tauri ビルドは重く、 # 毎コミットで回すと待ち時間と CI 時間の両方を食う。 on: push: tags: - "v*.*" env: CARGO_TERM_COLOR: always jobs: build: # tauri-action が GitHub Release を作成(= installer を Assets に添付)するのに要る。 permissions: contents: write env: # **秘密そのものではなく「在るか」だけを env へ出す。** # `secrets` は step の `if:` から参照できる保証が無いので、 # job の env へ真偽値として畳んでから `env.` で見る。 # # **5 つ全部を数える。** 一部だけ揃った状態で有効化してはいけない — # Tauri の ENVIRONMENT_VARIABLES.md が # 「APPLE_ID を指定するなら APPLE_PASSWORD と APPLE_TEAM_ID も要る」と # 明記しており、欠けた 1 つは空文字として届いて公証の段で落ちる。 # 署名だけ通って公証が無い .app は Gatekeeper が結局拒むので、 # **部分的に有効化しても得るものが無く、リリースが割れるだけ**。 HAS_APPLE_SIGNING: >- ${{ secrets.APPLE_CERTIFICATE != '' && secrets.APPLE_CERTIFICATE_PASSWORD != '' && secrets.APPLE_ID != '' && secrets.APPLE_PASSWORD != '' && secrets.APPLE_TEAM_ID != '' }} strategy: fail-fast: false matrix: platform: [ubuntu-latest, windows-latest, macos-latest] runs-on: ${{ matrix.platform }} steps: # タグから版を復元するため、浅いクローンでもタグ自体は要る。 # actions/checkout はトリガーとなったタグを取ってくるので追加設定は不要。 - uses: actions/checkout@v4 # タグ v0.1.0 → APP_VERSION=0.1.0(先頭の v を除去)。 # 後続の step で ${{ env.APP_VERSION }} を参照できる。 - name: Extract version from tag shell: bash run: | VERSION="${GITHUB_REF_NAME#v}" if [ -z "$VERSION" ]; then echo "タグからバージョンを取れない: $GITHUB_REF_NAME" exit 1 fi # Tauri は 3 要素 semver (X.Y.Z) を要求する。タグが v0.1 のように短ければ 0 で補う。 case "$VERSION" in *.*.*) ;; # 既に X.Y.Z *.*) VERSION="${VERSION}.0" ;; # X.Y → X.Y.0 *) VERSION="${VERSION}.0.0" ;; # X → X.0.0 esac echo "APP_VERSION=${VERSION}" >> "$GITHUB_ENV" echo "semver: ${VERSION}" # フロントは bun で回す(tauri.conf.json の beforeBuildCommand が `bun run build`)。 # ロックファイルは bun.lock 1 つだけ(package-lock.json は 2026-08-05 に削除した)。 - name: Setup Bun uses: oven-sh/setup-bun@v2 with: bun-version: latest - name: Setup Rust uses: dtolnay/rust-toolchain@stable # **ワークスペースはルートに 1 つだけ**(members = apps/gui-tauri/src-tauri, # crates/fuseforks-core)。target もルートに 1 つなので、キャッシュ対象も 1 つでよい。 - name: Rust cache uses: swatinem/rust-cache@v2 # Linux は Tauri(WebKitGTK)のシステム依存が要る。Windows / macOS は不要。 - name: Install dependencies (Ubuntu only) if: matrix.platform == 'ubuntu-latest' run: | sudo apt-get update sudo apt-get install -y \ libwebkit2gtk-4.1-dev \ build-essential \ curl wget file \ libssl-dev \ libgtk-3-dev \ libayatana-appindicator3-dev \ librsvg2-dev # **コアのテストを先に走らせる。** 壊れていればここで落ち、 # 重い Tauri ビルドに進まない(3 OS 分の時間を無駄にしない)。 - name: Test workspace run: cargo test --workspace - name: Install frontend dependencies working-directory: ./apps/gui-tauri run: bun install --frozen-lockfile # フロントの単体テスト。ここも Tauri ビルドの手前に置く。 - name: Test frontend working-directory: ./apps/gui-tauri run: bun run test # 配布ビルドの version をタグに揃える(installer に正しい版番号が乗る)。 # **CI 内だけの書き換えでコミットはしない。** # # 画面に出る版(StatusBar の `Version:`)はこことは別経路で、 # vite.config.ts が `git describe --tags` から取る。タグを打った # コミットをビルドしているので、両者は同じ値になる。 # # 実行は bun。node は runner に最初から入っているが、**この workflow が # 明示的に用意しているのは bun だけ**なので、宣言していない道具に依存しない。 - name: Sync app version to tag working-directory: ./apps/gui-tauri/src-tauri shell: bash run: | bun -e "const f='tauri.conf.json';const fs=require('fs');const j=JSON.parse(fs.readFileSync(f));j.version=process.env.APP_VERSION;fs.writeFileSync(f,JSON.stringify(j,null,2)+'\n')" # **macOS の署名と公証。秘密が揃っているときだけ環境変数を生やす。** # # 空文字を渡してはいけない — Tauri の `macos/sign.rs` は # `var_os("APPLE_CERTIFICATE")` で見ており、**空文字も `Some` を返す**ので # 「証明書が指定された」と読んで取り込みに行き、macOS だけが落ちる。 # `env:` へ直に書くと未設定の秘密は空文字になるため、この分岐が要る。 # タグ push が実質リリースなので、秘密の未設定でリリースが片肺になる形を # 構造で塞ぐ(releaseDraft: true を入れたのと同じ規律)。 # # **`APPLE_CERTIFICATE` は改行なしの base64 でなければならない** # (`openssl base64 -A`)。折り返した base64 は $GITHUB_ENV の # 1 行形式に載らない。 # # 公証は Apple ID 経路(`APPLE_ID` + `APPLE_PASSWORD` + `APPLE_TEAM_ID`)。 # `APPLE_PASSWORD` は App 固有パスワードで、Apple ID の本パスワードではない。 # `APPLE_SIGNING_IDENTITY` は置いていない — # 渡すと証明書と一致するかの検証にしか使われず、省略すると証明書から推論される。 - name: Configure Apple signing and notarization if: matrix.platform == 'macos-latest' && env.HAS_APPLE_SIGNING == 'true' shell: bash env: CERT: ${{ secrets.APPLE_CERTIFICATE }} CERT_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} ID: ${{ secrets.APPLE_ID }} APP_PASSWORD: ${{ secrets.APPLE_PASSWORD }} TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} run: | { echo "APPLE_CERTIFICATE=${CERT}" echo "APPLE_CERTIFICATE_PASSWORD=${CERT_PASSWORD}" echo "APPLE_ID=${ID}" echo "APPLE_PASSWORD=${APP_PASSWORD}" echo "APPLE_TEAM_ID=${TEAM_ID}" } >> "$GITHUB_ENV" echo "Apple signing: enabled" # 秘密が無い村(fork など)では署名せずに進む。**黙って進まない** — # 公証されていない .app は macOS が起動を拒むので、 # 「署名されなかった」ことがログから読めないと出荷物の性質が分からなくなる。 - name: Report unsigned macOS build if: matrix.platform == 'macos-latest' && env.HAS_APPLE_SIGNING != 'true' shell: bash run: | echo "Apple signing: DISABLED (secrets absent) -- the .app will not be notarized" # Tauri アプリをビルドし、タグの GitHub Release に installer を添付する。 # projectPath で src-tauri の親を指す。tagName を渡すと Release を作成/更新し、 # 3 OS の bundle が同じ Release の Assets に集まる(永続リンクになる)。 # # **releaseDraft: true は片肺公開を塞ぐためのもの**(2026-08-13)。 # このジョブは fail-fast: false の matrix で **3 OS が独立に**ここを呼ぶので、 # false だと **1 OS 通っただけで Release が public になる**。実際 2 回踏んだ: # - v0.1.3 (e935d2d): mac / ubuntu が赤、**Windows だけ完走して公開された** # - v0.1.5 (06489ce): ubuntu がランナーの DNS で落ちた # (index.crates.io が引けず依存解決の前で終了)。このときは # **落ちた側が先だったので気づけた** — 順番が逆なら欠けたまま出ていた # draft なら 3 OS 分の Assets が揃ったのを人が見てから publish できる。 # **タグは push した時点で残る**ので、publish しないことは「その版が # 存在しなかった」を意味しない(v0.1.2 の破棄で確かめた線引き)。 - name: Build Tauri app (+ GitHub Release) uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: projectPath: ./apps/gui-tauri tagName: ${{ github.ref_name }} releaseName: "Outcasts Fuseforks ${{ github.ref_name }}" releaseBody: "各 OS の installer を下の Assets からダウンロードしてください。" # **人が publish する。** 上のコメントの理由による。 releaseDraft: true prerelease: false # **artifact のアップロードは意図的に置いていない。** installer は上の # tauri-action が Release の Assets へ添付済みで(ストレージ枠の外・無期限)、 # artifact は同じものの 7 日限りの複製にしかならない。しかも private リポの # Actions ストレージ無料枠は 500 MB で、bundle/ 丸ごと(ubuntu は # deb + rpm + AppImage で約 195 MB)を 3 OS × 数タグ積むと必ず超える — # 実際に v0.0.5 のビルドが 679 MB / 500 MB の超過で落ちた(2026-08-07)。 # 成果物が要るときは Release の Assets から取ること。