Skip to content
.claude/skills/electron-distribution/SKILL.md logo

.claude/skills/electron-distribution/SKILL.md

Electronアプリケーションの配布・自動更新専門知識 📚 リソース参照: このスキルには以下のリソースが含まれています。 必要に応じて該当するリソースを参照してください: - `.claude/skills/electron-distribution/resources/auto-update.md`: 自動更新実装詳細 - `.claude/skills/electron-distribution/resources/release-channels.md`: リリースチャネル管理 - `.claude/skills/electron-distribution/resourc...

SKILL.md

Full skill instructions

.claude/​skills/​electron-distribution/​SKILL.md

Electronアプリケーションの配布・自動更新専門知識


概要

目的

Electronアプリケーションを効率的に配布し、 シームレスな自動更新を提供する。

対象者

  • Electronアプリ開発者
  • DevOpsエンジニア
  • リリースマネージャー

配布方法の選択

配布チャネル比較

方法メリットデメリット推奨ケース
GitHub Releases無料、簡単帯域制限ありOSS、小規模
S3/​CloudFront高速、スケーラブルコスト中〜大規模
Mac App Store信頼性、発見性審査、制約macOSメイン
Microsoft Store信頼性、自動更新審査Windowsメイン
自前サーバー完全制御運用負荷エンタープライズ

自動更新(electron-updater)

基本設定

# electron-builder.yml
publish:
  - provider: github
    owner: your-username
    repo: your-repo
    releaseType: release

更新サービス実装

// main/​services/​updater.ts
import { autoUpdater, UpdateInfo } from "electron-updater";
import { app, BrowserWindow, dialog } from "electron";
import log from "electron-log";

// ログ設定
autoUpdater.logger = log;
log.transports.file.level = "info";

// 更新チェック設定
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = true;

export class UpdateService {
  private mainWindow: BrowserWindow | null = null;

  constructor(win: BrowserWindow) {
    this.mainWindow = win;
    this.setupEventListeners();
  }

  private setupEventListeners(): void {
    // 更新確認中
    autoUpdater.on("checking-for-update", () => {
      this.sendToRenderer("update-status", { status: "checking" });
    });

    // 更新あり
    autoUpdater.on("update-available", (info: UpdateInfo) => {
      this.sendToRenderer("update-status", {
        status: "available",
        version: info.version,
        releaseNotes: info.releaseNotes,
      });

      // ユーザーに確認
      dialog
        .showMessageBox(this.mainWindow!, {
          type: "info",
          title: "更新があります",
          message: `バージョン ${info.version} が利用可能です。ダウンロードしますか?`,
          buttons: ["後で", "ダウンロード"],
          defaultId: 1,
        })
        .then(({ response }) => {
          if (response === 1) {
            autoUpdater.downloadUpdate();
          }
        });
    });

    // 更新なし
    autoUpdater.on("update-not-available", () => {
      this.sendToRenderer("update-status", { status: "up-to-date" });
    });

    // ダウンロード進捗
    autoUpdater.on("download-progress", (progress) => {
      this.sendToRenderer("update-progress", {
        percent: progress.percent,
        bytesPerSecond: progress.bytesPerSecond,
        total: progress.total,
        transferred: progress.transferred,
      });
    });

    // ダウンロード完了
    autoUpdater.on("update-downloaded", (info: UpdateInfo) => {
      this.sendToRenderer("update-status", {
        status: "downloaded",
        version: info.version,
      });

      dialog
        .showMessageBox(this.mainWindow!, {
          type: "info",
          title: "更新の準備完了",
          message: "更新をインストールするにはアプリを再起動してください。",
          buttons: ["後で", "今すぐ再起動"],
          defaultId: 1,
        })
        .then(({ response }) => {
          if (response === 1) {
            autoUpdater.quitAndInstall(false, true);
          }
        });
    });

    // エラー
    autoUpdater.on("error", (error) => {
      log.error("Update error:", error);
      this.sendToRenderer("update-status", {
        status: "error",
        message: error.message,
      });
    });
  }

  private sendToRenderer(channel: string, data: unknown): void {
    this.mainWindow?.webContents.send(channel, data);
  }

  // 手動更新チェック
  async checkForUpdates(): Promise<void> {
    try {
      await autoUpdater.checkForUpdates();
    } catch (error) {
      log.error("Check for updates failed:", error);
    }
  }

  // ダウンロード開始
  downloadUpdate(): void {
    autoUpdater.downloadUpdate();
  }

  // インストール
  quitAndInstall(): void {
    autoUpdater.quitAndInstall(false, true);
  }
}

// メインプロセスで初期化
app.whenReady().then(() => {
  const mainWindow = createMainWindow();
  const updateService = new UpdateService(mainWindow);

  // 起動時に更新チェック(遅延)
  setTimeout(() => {
    updateService.checkForUpdates();
  }, 3000);

  // IPC登録
  ipcMain.handle("update:check", () => updateService.checkForUpdates());
  ipcMain.handle("update:download", () => updateService.downloadUpdate());
  ipcMain.handle("update:install", () => updateService.quitAndInstall());
});

Renderer側UI

// renderer/​components/​UpdateNotification.tsx
import { useEffect, useState } from "react";

interface UpdateStatus {
  status:
    | "checking"
    | "available"
    | "downloading"
    | "downloaded"
    | "up-to-date"
    | "error";
  version?: string;
  message?: string;
}

interface UpdateProgress {
  percent: number;
  bytesPerSecond: number;
  total: number;
  transferred: number;
}

export function UpdateNotification() {
  const [status, setStatus] = useState<UpdateStatus | null>(null);
  const [progress, setProgress] = useState<UpdateProgress | null>(null);

  useEffect(() => {
    const cleanupStatus = window.electronAPI.onUpdateStatus(setStatus);
    const cleanupProgress = window.electronAPI.onUpdateProgress(setProgress);

    return () => {
      cleanupStatus();
      cleanupProgress();
    };
  }, []);

  if (!status || status.status === "up-to-date") {
    return null;
  }

  return (
    <div className="update-notification">
      {status.status === "checking" && <p>更新を確認中...</​p>}

      {status.status === "available" && (
        <div>
          <p>バージョン {status.version} が利用可能です</​p>
          <button onClick={() => window.electronAPI.downloadUpdate()}>
            ダウンロード
          </​button>
        </​div>
      )}

      {status.status === "downloading" && progress && (
        <div>
          <p>ダウンロード中: {Math.round(progress.percent)}%</​p>
          <progress value={progress.percent} max={100} />
        </​div>
      )}

      {status.status === "downloaded" && (
        <div>
          <p>更新の準備完了</​p>
          <button onClick={() => window.electronAPI.installUpdate()}>
            再起動してインストール
          </​button>
        </​div>
      )}

      {status.status === "error" && (
        <p className="error">更新エラー: {status.message}</​p>
      )}
    </​div>
  );
}

リリースチャネル

チャネル設定

# electron-builder.yml
publish:
  - provider: github
    owner: your-username
    repo: your-repo
    channel: ${channel}
// main/​services/​updater.ts
// チャネル設定
function setUpdateChannel(channel: "stable" | "beta" | "alpha"): void {
  autoUpdater.channel = channel;
  autoUpdater.allowPrerelease = channel !== "stable";
  autoUpdater.allowDowngrade = false;
}

// ユーザー設定から読み込み
const userSettings = store.get("updateChannel", "stable");
setUpdateChannel(userSettings);

バージョニング戦略

# Semantic Versioning
stable:  1.0.0, 1.0.1, 1.1.0, 2.0.0
beta:    1.1.0-beta.1, 1.1.0-beta.2
alpha:   1.1.0-alpha.1, 2.0.0-alpha.1

# package.json
{
  "version": "1.1.0-beta.1"
}

GitHub Releases配布

electron-builder.yml

publish:
  - provider: github
    owner: your-org
    repo: your-app
    releaseType: release # or draft, prerelease
    private: false

リリースワークフロー

# .github/​workflows/​release.yml
name: Release

on:
  push:
    tags:
      - "v*"

jobs:
  release:
    strategy:
      matrix:
        include:
          - os: macos-latest
            platform: mac
          - os: windows-latest
            platform: win
          - os: ubuntu-latest
            platform: linux

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/​checkout@v4

      - uses: actions/​setup-node@v4
        with:
          node-version: "20"
          cache: "npm"

      - run: npm ci
      - run: npm run build

      - name: Publish
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          # macOS署名
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
          CSC_LINK: ${{ secrets.MAC_CERTS }}
          CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTS_PASSWORD }}
          # Windows署名
          WIN_CERT_FILE: ${{ secrets.WIN_CERT_FILE }}
          WIN_CERT_PASSWORD: ${{ secrets.WIN_CERT_PASSWORD }}
        run: npm run publish -- --${{ matrix.platform }}

S3/​CloudFront配布

設定

# electron-builder.yml
publish:
  - provider: s3
    bucket: your-app-releases
    region: us-east-1
    acl: public-read
    path: /​releases/​${os}/​${arch}

CloudFront設定(Terraform例)

resource "aws_cloudfront_distribution" "releases" {
  origin {
    domain_name = aws_s3_bucket.releases.bucket_regional_domain_name
    origin_id   = "S3-releases"
  }

  enabled = true

  default_cache_behavior {
    allowed_methods        = ["GET", "HEAD"]
    cached_methods         = ["GET", "HEAD"]
    target_origin_id       = "S3-releases"
    viewer_protocol_policy = "redirect-to-https"

    forwarded_values {
      query_string = false
      cookies {
        forward = "none"
      }
    }
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }

  viewer_certificate {
    cloudfront_default_certificate = true
  }
}

カスタム更新サーバー

// update-server/​server.ts
import express from "express";
import { S3Client, GetObjectCommand } from "@aws-sdk/​client-s3";

const app = express();
const s3 = new S3Client({ region: "us-east-1" });

// 更新情報エンドポイント
app.get("/​update/:platform/:version", async (req, res) => {
  const { platform, version } = req.params;

  try {
    // 最新バージョン取得
    const latestVersion = await getLatestVersion(platform);

    if (compareVersions(latestVersion, version) > 0) {
      res.json({
        version: latestVersion,
        files: await getDownloadUrls(platform, latestVersion),
        releaseDate: await getReleaseDate(latestVersion),
        releaseNotes: await getReleaseNotes(latestVersion),
      });
    } else {
      res.status(204).send();
    }
  } catch (error) {
    res.status(500).json({ error: "Failed to check updates" });
  }
});

// ダウンロードエンドポイント
app.get("/​download/:platform/:version/:file", async (req, res) => {
  const { platform, version, file } = req.params;

  // 署名付きURL生成
  const signedUrl = await generateSignedUrl(
    `releases/​${platform}/​${version}/​${file}`,
  );

  res.redirect(signedUrl);
});

app.listen(3000);

Mac App Store配布

制約事項

機能通常配布MAS
自動更新electron-updaterApp Store
サンドボックス任意必須
ハードニングランタイム推奨必須
署名Developer IDMac App Store
ネイティブモジュール可制限あり

electron-builder設定

# electron-builder.yml
mas:
  target: mas

  hardenedRuntime: true
  gatekeeperAssess: false

  entitlements: build/​entitlements.mas.plist
  entitlementsInherit: build/​entitlements.mas.inherit.plist

  # MAS用署名
  identity: "3rd Party Mac Developer Application: Company (TEAM_ID)"

  # プロビジョニングプロファイル
  provisioningProfile: build/​embedded.provisionprofile

masDev:
  identity: "Mac Developer: Your Name (XXXXXXXXXX)"
  provisioningProfile: build/​embedded-dev.provisionprofile
<!-- build/​entitlements.mas.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/​DTDs/​PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>com.apple.security.app-sandbox</​key>
    <true/>
    <key>com.apple.security.network.client</​key>
    <true/>
    <key>com.apple.security.files.user-selected.read-write</​key>
    <true/>
</​dict>
</​plist>

Microsoft Store配布

AppX設定

# electron-builder.yml
win:
  target:
    - target: appx
      arch:
        - x64
        - arm64

appx:
  applicationId: CompanyName.AppName
  displayName: My Electron App
  identityName: 12345CompanyName.AppName
  publisher: CN=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
  publisherDisplayName: Company Name
  languages:
    - ja-JP
    - en-US
  addAutoLaunchExtension: false
  showNameOnTiles: true
  backgroundColor: "#ffffff"

リリースチェックリスト

リリース前

  • バージョン番号更新
  • CHANGELOGの更新
  • 全プラットフォームでテスト
  • コード署名の確認
  • 依存関係のセキュリティ監査
  • リリースノート作成

リリース後

  • ダウンロードリンク確認
  • 自動更新テスト
  • インストールテスト(クリーン環境)
  • アップグレードテスト(既存インストール)
  • クラッシュレポート監視

関連リソース

詳細ドキュメント

  • resources/​auto-update.md - 自動更新詳細
  • resources/​release-channels.md - チャネル管理
  • resources/​store-distribution.md - ストア配布

テンプレート・スクリプト

  • templates/​update-server.ts - 更新サーバー
  • scripts/​release.sh - リリーススクリプト

More skills from majiayu000

xiaohongshu logo
majiayu000/claude-arsenal

xiaohongshu

xiaohongshu

286 148
View
agent-task-conductor logo
majiayu000/claude-skill-registry

agent-task-conductor

Conduct multi-agent task orchestration and workflow coordination.

663 1
View
conductor-setup logo
majiayu000/claude-skill-registry

conductor-setup

Initialize project with Conductor artifacts (product definition,

663 1
View
animation-designer logo
majiayu000/claude-skill-registry

animation-designer

Expert in web animations, transitions, and motion design using Framer Motion and CSS

663 1
View
diagramming logo
majiayu000/claude-skill-registry

diagramming

Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...

663 1
View
h3-pg logo
majiayu000/claude-skill-registry-data

h3-pg

PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.

23 1
View
conductor-development logo
majiayu000/claude-skill-registry

conductor-development

Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...

663 1
View
conductor-status logo
majiayu000/claude-skill-registry

conductor-status

Display project status, active tracks, and next actions

663 1
View
dockerization logo
majiayu000/claude-skill-registry

dockerization

Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.

663 1
View

Popular AI tools

Kaiber logo
Video

Kaiber

Generate, edit, and beat-sync AI video with leading models in one workspace.

Paid
View
Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View

Transform Your Design with AI Designer by ImgCreator.ai

Freemium
View
Akool AI logo
Content & writing

Akool AI

Revolutionizing Video Production with AI-Powered Creativity

Paid
View

Extend an image past the frame and let AI fill the new aspect ratio.

Freemium
View
StarByFace logo
Security

StarByFace

Discover your celebrity doppelgänger with StarByFace!

Free
View
C

ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.

Freemium
View
Opus Clip logo
Coding & apps

Opus Clip

Opus.ai: Revolutionize Your Web Experience

Free
View