Repository navigation
231 lines (201 loc) · 8.65 KB
/
Copy pathrelease.yml
File metadata and controls
231 lines (201 loc) · 8.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
name: Release
on:
push:
tags:
- 'v*'
permissions:
contents: write
jobs:
build:
name: Build binary (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
# Two targets, both built natively on a standard runner.
#
# aarch64 Linux is deliberately absent. It needs `cross`, and the keyring
# dependency reaches libdbus-sys, which wants libdbus for the target
# architecture inside the cross container. Nothing supplies it, so that
# leg fails to link and the publish guard below then blocks the whole
# release over one missing archive. Intel macOS is absent for the same
# reason it is cheap to drop: nobody has asked for it.
#
# Users on either platform build from source, which the README covers.
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: macos-latest
target: aarch64-apple-darwin
steps:
# actions/checkout v7.0.1
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
# dtolnay/rust-toolchain pinned
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@6c977a6ca4077a0ceb28ffbe03f59d46e9ac8772
with:
toolchain: "1.97.1"
targets: ${{ matrix.target }}
- name: Install Linux dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libdbus-1-dev pkg-config
- name: Build release binary
run: cargo build --release --target ${{ matrix.target }} --locked
- name: Package artifact
shell: bash
run: |
TAG="${GITHUB_REF#refs/tags/}"
VERSION="${TAG#v}"
ARCHIVE_NAME="mooshik-${VERSION}-${{ matrix.target }}.tar.gz"
mkdir -p dist
BIN_PATH="target/${{ matrix.target }}/release/mooshik"
if [ "${{ runner.os }}" = "Linux" ]; then
strip "$BIN_PATH"
fi
cp "$BIN_PATH" ./mooshik
tar -czf "dist/${ARCHIVE_NAME}" mooshik
rm mooshik
cd dist
if command -v sha256sum >/dev/null 2>&1; then
sha256sum "${ARCHIVE_NAME}" > "${ARCHIVE_NAME}.sha256"
else
shasum -a 256 "${ARCHIVE_NAME}" > "${ARCHIVE_NAME}.sha256"
fi
- name: Upload artifact
# actions/upload-artifact v4.6.1
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1
with:
name: artifacts-${{ matrix.target }}
path: dist/*
if-no-files-found: error
# The MCP servers are Python, and a binary-only release cannot run any of
# them. They are pure Python and therefore arch- and OS-independent, so they
# are built ONCE here and shipped as a SINGLE separate asset rather than
# embedded four times into the platform tarballs. Three reasons:
#
# 1. Embedding identical bytes in four archives implies the servers are
# platform-specific. They are not; every wheel is `py3-none-any`.
# 2. The platform tarballs keep their exact current shape — one `mooshik`
# binary and nothing else — so the binary-only install path cannot
# regress, and anyone extracting a tarball by hand sees no change.
# 3. No cross-job artifact plumbing into the build matrix; this job runs
# independently and in parallel with the four binary builds.
#
# Wheels rather than source trees: the target machine then needs no build
# backend (`pip install` on an sdist would want setuptools and a compiler
# toolchain for anything that is not pure Python). Only OUR four packages
# are vendored — their third-party dependencies (mcp, google-genai,
# google-adk) still resolve from PyPI at install time, so `install.sh`'s
# Python step needs network access.
wheels:
name: Build Python wheels (MCP servers)
runs-on: ubuntu-latest
steps:
# actions/checkout v7.0.1
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
# actions/setup-python v5 (pinned by tag SHA, matching ci.yml)
- name: Set up Python
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065
with:
python-version: "3.13"
- name: Build wheels
shell: bash
run: |
set -euo pipefail
TAG="${GITHUB_REF#refs/tags/}"
VERSION="${TAG#v}"
BUNDLE="mooshik-python-${VERSION}.tar.gz"
python -m pip install --upgrade pip wheel setuptools
mkdir -p wheelhouse dist
# --no-deps: vendor our four packages only. Third-party pins are
# resolved from PyPI on the user's machine by install.sh.
pip wheel --no-deps --wheel-dir wheelhouse \
./mooshik-common \
./mcp-servers/news \
./mcp-servers/artifacts \
./mcp-servers/coder
ls -l wheelhouse
count=$(find wheelhouse -maxdepth 1 -name '*.whl' | wc -l)
if [ "$count" -ne 4 ]; then
echo "::error::expected 4 wheels, built ${count}"
exit 1
fi
# A wheel that lost its console script would install cleanly and then
# be unaddressable from [mcp_servers.*], which is exactly the failure
# this whole asset exists to prevent. Read the metadata directly.
python - <<'PY'
import pathlib, sys, zipfile
want = {"mooshik-news-mcp", "mooshik-artifacts-mcp", "mooshik-coder-mcp"}
found = set()
for wheel in sorted(pathlib.Path("wheelhouse").glob("*.whl")):
with zipfile.ZipFile(wheel) as archive:
for name in archive.namelist():
if name.endswith(".dist-info/entry_points.txt"):
text = archive.read(name).decode("utf-8")
for line in text.splitlines():
if "=" in line:
found.add(line.split("=", 1)[0].strip())
missing = sorted(want - found)
if missing:
print(f"::error::console scripts missing from the built wheels: {missing}")
sys.exit(1)
print("console scripts present:", sorted(want))
PY
tar -czf "dist/${BUNDLE}" -C wheelhouse .
cd dist
sha256sum "${BUNDLE}" > "${BUNDLE}.sha256"
- name: Upload artifact
# actions/upload-artifact v4.6.1
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1
with:
name: artifacts-python
path: dist/*
if-no-files-found: error
publish:
name: Publish GitHub Release
needs: [build, wheels]
runs-on: ubuntu-latest
steps:
# actions/checkout v7.0.1
- name: Checkout repository
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
# actions/download-artifact v4.1.9
- name: Download all artifacts
uses: actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806
with:
path: release-artifacts
# Both globs are deliberately shape-based rather than name-based, so the
# Python wheel bundle (mooshik-python-<version>.tar.gz) and its .sha256
# are consolidated by the same two lines as the platform tarballs — that
# is what keeps checksums.txt covering every published asset.
- name: Consolidate archives and checksums
shell: bash
run: |
set -euo pipefail
mkdir -p release-files
find release-artifacts -type f -name '*.tar.gz' -exec cp {} release-files/ \;
find release-artifacts -type f -name '*.sha256' -exec cat {} + > release-files/checksums.txt
# 2 platform tarballs + 1 Python bundle. A missing asset here means a
# build job silently produced nothing, and install.sh would then fail
# its checksum grep on the user's machine instead.
archives=$(find release-files -maxdepth 1 -name '*.tar.gz' | wc -l)
sums=$(wc -l < release-files/checksums.txt)
if [ "$archives" -ne 3 ] || [ "$sums" -ne 3 ]; then
echo "::error::expected 3 archives and 3 checksum lines; got ${archives} and ${sums}"
cat release-files/checksums.txt
exit 1
fi
cat release-files/checksums.txt
- name: Create GitHub Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: bash
run: |
TAG="${GITHUB_REF#refs/tags/}"
gh release create "$TAG" release-files/* \
--title "Mooshik $TAG" \
--generate-notes