Skip to content

Commit f4a7568

Browse files
ci: publish to a Homebrew tap after each release (#313) (#322)
Adds Homebrew as a distribution channel. The formula lives in a dedicated tap repo, mcpp-community/homebrew-mcpp; this side only pings it. brew install mcpp-community/mcpp/mcpp-m One command: a tap whose repo is named homebrew-<name> is cloned on demand by `brew install <user>/<repo>/<formula>`, so no separate `brew tap` step is needed. That auto-tap is also why the formula does not live in this repo — tapping a repo that isn't named homebrew-* requires the user to pass its URL, which turns installation into two commands and a URL nobody will remember. Two things the tap had to get right, both learned from scripts/aur: - mcpp derives MCPP_HOME from the *canonicalized* path of the running binary (home.cppm + platform/fs.cppm resolve symlinks). Linked straight into bin, that path is the Cellar, so the registry sandbox, caches and every downloaded toolchain would land in a versioned directory that `brew upgrade` deletes. The formula keeps the release tree under libexec and ships a launcher that pins MCPP_HOME/MCPP_VENDORED_XLINGS, same shape as scripts/aur/mcpp-bin/mcpp.sh. - The name. homebrew-core owns `mcpp` (Matsui's C preprocessor, ~675 installs/yr, not deprecated), exactly as extra/mcpp does on Arch. The formula is `mcpp-m`, with Aliases/{mcpp,mcpp-bin} pointing at it; the installed command is still `mcpp`. The workflow does not write the formula: it sends a repository_dispatch and the tap rewrites itself from the release's .sha256 sidecars. It hangs off `workflow_run: [release] completed` rather than `release: published` for the reason aur-publish.yml already documents — the macOS and aarch64 assets are uploaded by later jobs, and the tap needs every sidecar to exist. HOMEBREW_TAP_TOKEN is optional by design: without it this job logs a notice and exits 0, and the tap's daily schedule picks the release up within 24h. A release should not fail over a credential it doesn't itself need. Co-authored-by: sunrisepeak <speakshen@163.com>
1 parent 0550fc7 commit f4a7568

4 files changed

Lines changed: 137 additions & 4 deletions

File tree

.agents/skills/mcpp-release/SKILL.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,24 @@ git commit -am "ci: workspace mcpp bootstrap pin -> $NEW_VERSION (released, mirr
231231
资产 CDN 上可能还要几分钟才更新。紧接着跑的 CI 可能仍拿到旧索引并报
232232
`package 'mcpp@X.Y.Z' not found` —— 这不是 release 坏了,等指针稳定后重跑即可。
233233

234+
### 下游分发渠道(都是自动的,只需核验)
235+
236+
两条渠道都挂在 `release` workflow 的 `workflow_run: completed` 上,不需要人工推:
237+
238+
| 渠道 | workflow | 核验 |
239+
|------|----------|------|
240+
| AUR (`mcpp-bin` / `mcpp-m`) | `.github/workflows/aur-publish.yml` | `gh run list --workflow aur-publish.yml --limit 1` |
241+
| Homebrew tap (`mcpp-m`) | `.github/workflows/homebrew-publish.yml` → ping [`mcpp-community/homebrew-mcpp`](https://github.com/mcpp-community/homebrew-mcpp) | `gh api repos/mcpp-community/homebrew-mcpp/contents/Formula/mcpp-m.rb --jq .content \| base64 -d \| grep '^ version'` |
242+
243+
Homebrew 那条**不写公式**,只发一个 `repository_dispatch`;公式重写由 tap 仓库自己的
244+
`bump-formula.yml` 完成(它读 release 的 `.sha256` 边车)。这条 ping 依赖仓库 secret
245+
`HOMEBREW_TAP_TOKEN`**没配也不会让发布失败** —— tap 有每日 schedule 兜底,24h 内自己跟上。
246+
想立刻跟上就手动触发一次:
247+
248+
```bash
249+
gh workflow run bump-formula.yml -R mcpp-community/homebrew-mcpp
250+
```
251+
234252
### 常见失败原因
235253

236254
| 症状 | 原因 | 修复 |
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
name: homebrew-publish
2+
3+
# Tell the Homebrew tap (mcpp-community/homebrew-mcpp) that a release is out.
4+
#
5+
# The tap owns the formula rewrite — it reads the .sha256 sidecars from the
6+
# release and commits the new url/version itself. All this workflow does is
7+
# fire the starting gun, so nothing here needs to know what a formula is.
8+
#
9+
# Triggers on COMPLETION of the `release` workflow rather than on
10+
# `release: published`, for the same reason aur-publish.yml does: release.yml
11+
# creates the GitHub Release in its first job but uploads the macOS / aarch64
12+
# assets in later jobs, and the tap needs every sidecar to exist.
13+
#
14+
# Requires one repository secret:
15+
# HOMEBREW_TAP_TOKEN — fine-grained PAT scoped to mcpp-community/homebrew-mcpp
16+
# with "Contents: read and write" (repository_dispatch
17+
# is a write-level API).
18+
#
19+
# The secret is OPTIONAL. Without it this workflow logs a notice and exits 0;
20+
# the tap runs the same bump on a daily schedule, so a missing token costs
21+
# freshness (up to 24h), not correctness. That keeps a release from failing
22+
# over a credential the release itself doesn't need.
23+
24+
on:
25+
workflow_run:
26+
workflows: [release]
27+
types: [completed]
28+
workflow_dispatch:
29+
inputs:
30+
version:
31+
description: "Version to publish (default: [package].version in mcpp.toml)"
32+
required: false
33+
34+
concurrency:
35+
group: homebrew-publish
36+
cancel-in-progress: false
37+
38+
jobs:
39+
notify-tap:
40+
runs-on: ubuntu-latest
41+
# On the workflow_run trigger, only proceed if the release actually
42+
# succeeded (skip failed/cancelled release runs).
43+
if: >-
44+
github.event_name == 'workflow_dispatch' ||
45+
github.event.workflow_run.conclusion == 'success'
46+
steps:
47+
- name: Checkout released commit
48+
uses: actions/checkout@v4
49+
with:
50+
# workflow_run: the exact commit the release was built from.
51+
# workflow_dispatch: default ref (HEAD of the branch).
52+
ref: ${{ github.event.workflow_run.head_sha || github.ref }}
53+
54+
- name: Resolve version
55+
id: resolve
56+
run: |
57+
VER="${{ github.event.inputs.version }}"
58+
if [ -z "$VER" ]; then
59+
# mcpp.toml at the released commit carries the right version.
60+
VER=$(grep -m1 -E '^\s*version\s*=' mcpp.toml | sed -E 's/.*"([^"]+)".*/\1/')
61+
fi
62+
[ -n "$VER" ] || { echo "cannot resolve version"; exit 1; }
63+
echo "version=$VER" >> "$GITHUB_OUTPUT"
64+
echo ":: version $VER"
65+
66+
- name: Ping the tap
67+
env:
68+
TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }}
69+
VER: ${{ steps.resolve.outputs.version }}
70+
run: |
71+
set -eu
72+
if [ -z "${TAP_TOKEN}" ]; then
73+
echo "::notice::HOMEBREW_TAP_TOKEN is not configured; skipping the ping. mcpp-community/homebrew-mcpp bumps itself on a daily schedule, so ${VER} reaches the tap within 24h."
74+
exit 0
75+
fi
76+
curl -fsS -X POST \
77+
-H "Authorization: Bearer ${TAP_TOKEN}" \
78+
-H "Accept: application/vnd.github+json" \
79+
-H "X-GitHub-Api-Version: 2022-11-28" \
80+
https://api.github.com/repos/mcpp-community/homebrew-mcpp/dispatches \
81+
-d "{\"event_type\":\"mcpp-release\",\"client_payload\":{\"version\":\"${VER}\"}}"
82+
echo ":: dispatched mcpp-release ${VER} to mcpp-community/homebrew-mcpp"
83+
echo "Pinged the Homebrew tap for **${VER}**." >> "$GITHUB_STEP_SUMMARY"

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,23 @@ Installs into `~/.mcpp/` and adds it to your shell PATH. Deleting `~/.mcpp` unin
8181
</details>
8282

8383
<details>
84-
<summary><b>Option 2</b> — Arch Linux (AUR)</summary>
84+
<summary><b>Option 2</b> — Homebrew (macOS / Linux)</summary>
85+
86+
```bash
87+
brew install mcpp-community/mcpp/mcpp-m
88+
```
89+
90+
One command — it taps [`mcpp-community/homebrew-mcpp`](https://github.com/mcpp-community/homebrew-mcpp)
91+
and installs the same prebuilt release binary. macOS needs Apple silicon +
92+
macOS 14; per-user data lives in `~/.mcpp/`.
93+
94+
Homebrew's `mcpp` is an unrelated C preprocessor, hence the `mcpp-m` formula
95+
name — the command it installs is still `mcpp`.
96+
97+
</details>
98+
99+
<details>
100+
<summary><b>Option 3</b> — Arch Linux (AUR)</summary>
85101

86102
```bash
87103
yay -S mcpp-bin # prebuilt release binary
@@ -95,7 +111,7 @@ On Arch the name `mcpp` is an unrelated C preprocessor, so the packages are
95111
</details>
96112

97113
<details>
98-
<summary><b>Option 3</b> — let an AI assistant install it for you</summary>
114+
<summary><b>Option 4</b> — let an AI assistant install it for you</summary>
99115

100116
Copy the following prompt to your AI coding assistant (Claude Code / Cursor / Copilot, etc.):
101117

README.zh-CN.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,23 @@ curl -fsSL https://github.com/mcpp-community/mcpp/releases/latest/download/insta
8181
</details>
8282

8383
<details>
84-
<summary><b>方式 2</b> — Arch Linux(AUR)</summary>
84+
<summary><b>方式 2</b> — Homebrew(macOS / Linux)</summary>
85+
86+
```bash
87+
brew install mcpp-community/mcpp/mcpp-m
88+
```
89+
90+
一条命令即可,会自动 tap [`mcpp-community/homebrew-mcpp`](https://github.com/mcpp-community/homebrew-mcpp)
91+
并安装同一份预编译 release 二进制。macOS 需要 Apple 芯片 + macOS 14;
92+
每个用户的数据仍在各自的 `~/.mcpp/`
93+
94+
Homebrew 上 `mcpp` 属于一个无关的 C 预处理器,所以公式名是 `mcpp-m`
95+
装出来的命令仍然是 `mcpp`
96+
97+
</details>
98+
99+
<details>
100+
<summary><b>方式 3</b> — Arch Linux(AUR)</summary>
85101

86102
```bash
87103
yay -S mcpp-bin # 预编译 release 二进制
@@ -95,7 +111,7 @@ Arch 上 `mcpp` 这个名字属于一个无关的 C 预处理器,所以包名
95111
</details>
96112

97113
<details>
98-
<summary><b>方式 3</b> — 让 AI 助手帮你安装</summary>
114+
<summary><b>方式 4</b> — 让 AI 助手帮你安装</summary>
99115

100116
将以下提示词复制给你的 AI 编码助手(Claude Code / Cursor / Copilot 等):
101117

0 commit comments

Comments
 (0)