diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f16456d..a592133 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,7 @@ on: paths-ignore: - "**.md" - "docs/**" + - "Casks/**" pull_request: branches: [main] workflow_dispatch: diff --git a/.github/workflows/cask.yml b/.github/workflows/cask.yml new file mode 100644 index 0000000..b458346 --- /dev/null +++ b/.github/workflows/cask.yml @@ -0,0 +1,80 @@ +name: update-cask + +# Keep Casks/wechat-multi.rb in lockstep with releases. The release workflow +# creates a *draft* GitHub release; when the maintainer publishes it, this job +# downloads the attached zip, recomputes its sha256, and bumps the cask on main. +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: "Release tag to sync the cask to (e.g. v2.0.0) — must already exist" + required: true + +permissions: + contents: write + +concurrency: + group: update-cask + cancel-in-progress: false + +jobs: + update-cask: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + with: + ref: main + # token with contents:write so the bump commit can be pushed back to main + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Resolve tag/version + id: ver + run: | + TAG="${{ github.event.release.tag_name || github.event.inputs.tag }}" + case "$TAG" in + v*.*.*) ;; + *) echo "::error::Tag '$TAG' is not a vX.Y.Z release tag — skipping"; exit 1 ;; + esac + echo "tag=$TAG" >> "$GITHUB_OUTPUT" + echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" + echo "Syncing cask to $TAG (version ${TAG#v})" + + - name: Download release asset and compute sha256 + id: sha + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + TAG="${{ steps.ver.outputs.tag }}" + ASSET="WeChat-Multi-${TAG}.zip" + gh release download "$TAG" \ + --repo "$GITHUB_REPOSITORY" \ + --pattern "$ASSET" \ + --dir "$RUNNER_TEMP" + SHA=$(sha256sum "$RUNNER_TEMP/$ASSET" | awk '{print $1}') + echo "sha256=$SHA" >> "$GITHUB_OUTPUT" + echo "Asset $ASSET sha256=$SHA" + + - name: Update cask file + run: | + CASK="Casks/wechat-multi.rb" + VERSION="${{ steps.ver.outputs.version }}" + SHA="${{ steps.sha.outputs.sha256 }}" + sed -i -E "s/^( version )\"[^\"]*\"/\1\"${VERSION}\"/" "$CASK" + sed -i -E "s/^( sha256 )\"[^\"]*\"/\1\"${SHA}\"/" "$CASK" + echo "----- updated cask -----" + cat "$CASK" + + - name: Commit and push if changed + run: | + if git diff --quiet -- Casks/wechat-multi.rb; then + echo "Cask already up to date for ${{ steps.ver.outputs.tag }} — nothing to commit" + exit 0 + fi + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add Casks/wechat-multi.rb + git commit -m "cask: bump to ${{ steps.ver.outputs.version }}" + git push origin HEAD:main diff --git a/Casks/wechat-multi.rb b/Casks/wechat-multi.rb new file mode 100644 index 0000000..24c2ac0 --- /dev/null +++ b/Casks/wechat-multi.rb @@ -0,0 +1,34 @@ +cask "wechat-multi" do + version "2.0.0" + sha256 "01bf16aec77295ff09e44948b59682679f27f7352cfda4cebf87cd33f5b6359c" + + url "https://github.com/ashinno/wechat-multi/releases/download/v#{version}/WeChat-Multi-v#{version}.zip" + name "WeChat Multi" + desc "Menu bar app to run multiple WeChat accounts side by side" + homepage "https://github.com/ashinno/wechat-multi" + + livecheck do + url :url + strategy :github_latest + end + + depends_on macos: ">= :ventura" + + app "WeChat Multi.app" + + caveats <<~EOS + WeChat Multi is ad-hoc signed, not notarized. The first time you launch it, + right-click "WeChat Multi" in Finder and choose Open to get past Gatekeeper + (you only need to do this once). + + It works by cloning /Applications/WeChat.app, so the official WeChat must be + installed for it to do anything. + EOS + + zap trash: [ + "~/Applications/WeChat Multi", + "~/Library/Containers/com.wechatmulti.clone*", + "~/Library/Preferences/com.wechatmulti.app.plist", + "~/Library/Saved Application State/com.wechatmulti.app.savedState", + ] +end diff --git a/README.md b/README.md index 798256f..7575560 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,18 @@ Don't want to compile? Grab the pre-built `.app` from drag it into `/Applications`, and double-click. macOS may ask you to right- click → Open the first time because it's ad-hoc signed. +### Homebrew + +```bash +brew tap ashinno/wechat-multi https://github.com/ashinno/wechat-multi +brew install --cask wechat-multi +``` + +Upgrade with `brew upgrade --cask wechat-multi`; the cask version is bumped +automatically by CI whenever a new release is published. To remove it later, +`brew uninstall --cask wechat-multi` (add `--zap` to also delete clones and +their signed-in sessions). + ## Features - 🍎 **Native menu bar app** — no Dock clutter, no Electron, ~190 KB binary @@ -249,7 +261,6 @@ swift test # run the full Core suite PRs welcome. A few directions if you're looking for ideas: - Sparkle auto-update (with EdDSA-signed appcast from CI) -- Homebrew cask - Notarization (would remove the right-click-Open ceremony) - Global hotkey to toggle the popover from anywhere diff --git a/README.zh-CN.md b/README.zh-CN.md index 8fdaeca..58569e6 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -55,6 +55,17 @@ Mac 版微信限制每次只能启动一个实例,第二次启动的进程会被 签名(没有 Apple 开发者证书签名),首次打开时 macOS 可能会提示拒绝, 请 **右键 → 打开** 一次绕过 Gatekeeper。 +### Homebrew + +```bash +brew tap ashinno/wechat-multi https://github.com/ashinno/wechat-multi +brew install --cask wechat-multi +``` + +升级:`brew upgrade --cask wechat-multi`(每次发布新版本时,CI 会自动更新 +Cask 版本号)。卸载:`brew uninstall --cask wechat-multi`(加 `--zap` 可一并 +删除克隆体及其登录数据)。 + ## 功能 - 🍎 **原生菜单栏应用** —— 不占 Dock、不用 Electron,二进制只有约 190 KB @@ -197,7 +208,6 @@ defaults delete com.wechatmulti.app 2>/dev/null - 通用二进制构建(目前仅支持 arm64) - 通过 `SMAppService` 实现"登录时启动" - 完整的 `.icns` 图标和"关于"面板插画 -- 支持 Homebrew Cask 安装 - 通知:首次克隆完成时弹出提示 ## 许可证