Skip to content

Commit 92f1335

Browse files
committed
release: v0.0.1
Signed-off-by: Sunrisepeak <speakshen@163.com>
0 parents  commit 92f1335

88 files changed

Lines changed: 15451 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci.yml

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
name: ci
2+
3+
# Self-host CI: mcpp builds mcpp. The bootstrap mcpp comes from
4+
# `xlings install mcpp` (xim:mcpp in the xlings package index), so
5+
# this workflow no longer depends on a previous-release tarball — the
6+
# chicken-and-egg now lives upstream in the xlings index.
7+
8+
on:
9+
push:
10+
branches: [ main ]
11+
pull_request:
12+
branches: [ main ]
13+
workflow_dispatch:
14+
15+
concurrency:
16+
group: ci-${{ github.workflow }}-${{ github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
build-and-test:
21+
name: build + test (linux x86_64, self-host)
22+
runs-on: ubuntu-24.04
23+
timeout-minutes: 60
24+
env:
25+
# MCPP_HOME pinned so the cache key below restores into the
26+
# same path mcpp resolves at runtime.
27+
MCPP_HOME: /home/runner/.mcpp
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
# Cache mcpp's sandbox: the toolchain (musl-gcc + binutils +
32+
# glibc + linux-headers + patchelf + ninja) takes minutes to
33+
# install on a cold runner. Key on the workspace manifest so a
34+
# toolchain change in mcpp.toml refreshes the cache; restore-keys
35+
# provide a layered fallback so near-misses still skip the slow
36+
# toolchain installs.
37+
- name: Cache mcpp sandbox
38+
uses: actions/cache@v4
39+
with:
40+
path: ~/.mcpp
41+
key: mcpp-sandbox-${{ runner.os }}-${{ hashFiles('mcpp.toml', '.xlings.json') }}
42+
restore-keys: |
43+
mcpp-sandbox-${{ runner.os }}-
44+
45+
# Cache xlings + its locally installed packages (xim:mcpp etc.).
46+
# Saves the xlings bootstrap roundtrip + the mcpp xpkg download
47+
# on hot runs.
48+
- name: Cache xlings
49+
uses: actions/cache@v4
50+
with:
51+
path: ~/.xlings
52+
key: xlings-${{ runner.os }}-${{ hashFiles('.xlings.json') }}
53+
restore-keys: |
54+
xlings-${{ runner.os }}-
55+
56+
- name: Bootstrap mcpp via xlings
57+
env:
58+
XLINGS_NON_INTERACTIVE: '1'
59+
run: |
60+
# xlings: install if not cached. The installer reads from
61+
# /dev/tty for an interactive prompt; XLINGS_NON_INTERACTIVE=1
62+
# skips that and runs `xlings self install` directly.
63+
if [ ! -x "$HOME/.xlings/subos/default/bin/xlings" ]; then
64+
curl -fsSL https://d2learn.org/xlings-install.sh | bash
65+
fi
66+
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
67+
xlings --version
68+
# xim:mcpp — `xlings install` is idempotent so cache hits skip
69+
# the download.
70+
xlings install mcpp -y
71+
MCPP="$HOME/.xlings/subos/default/bin/mcpp"
72+
test -x "$MCPP"
73+
"$MCPP" --version
74+
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
75+
echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV"
76+
77+
# Cache the build directory: precise key on src/ + manifest
78+
# so a no-source-change run lands on a full hit. Layered
79+
# restore-keys let mid-run partial hits keep BMI/dyndep state
80+
# for proper incremental builds.
81+
- name: Cache target/ (build artifacts + BMIs)
82+
uses: actions/cache@v4
83+
with:
84+
path: target
85+
key: mcpp-target-${{ runner.os }}-${{ hashFiles('src/**', 'tests/**', 'mcpp.toml', 'mcpp.lock') }}
86+
restore-keys: |
87+
mcpp-target-${{ runner.os }}-
88+
89+
- name: Build mcpp from source (self-host)
90+
run: |
91+
"$MCPP" build
92+
93+
- name: Unit + integration tests via `mcpp test`
94+
run: |
95+
"$MCPP" test
96+
97+
- name: E2E suite
98+
run: |
99+
# Point the e2e runner at the freshly-built binary, not the
100+
# bootstrap one. Tests cd into mktemp -d, so $MCPP must be
101+
# absolute or the relative path breaks under the temp cwd.
102+
MCPP=$(realpath "$(find target -type f -name mcpp | head -1)")
103+
test -x "$MCPP"
104+
export MCPP
105+
# Tests that set MCPP_HOME to a fresh tmpdir need an xlings
106+
# to bootstrap from; surface the xlings binary installed
107+
# above so they don't have to reinstall the sandbox.
108+
export MCPP_VENDORED_XLINGS="$XLINGS_BIN"
109+
test -x "$MCPP_VENDORED_XLINGS"
110+
# Pin the global default so test 28 (which exercises the
111+
# default-toolchain path) gets a deterministic GNU answer
112+
# instead of whatever auto-install picks on a fresh sandbox.
113+
"$MCPP" toolchain default gcc@16.1.0
114+
bash tests/e2e/run_all.sh
115+
116+
- name: Self-host smoke (freshly-built mcpp builds itself again)
117+
run: |
118+
MCPP=$(realpath "$(find target -type f -name mcpp | head -1)")
119+
"$MCPP" build
120+
"$MCPP" test

.github/workflows/release.yml

Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
name: release
2+
3+
# Self-host release: bootstrap mcpp from xlings (xim:mcpp), build the
4+
# musl-static artefact via `mcpp pack --target x86_64-linux-musl -o ...`,
5+
# inject xlings into the produced tarball for install.sh consumers,
6+
# smoke-test, upload.
7+
8+
on:
9+
push:
10+
tags: [ 'v*' ]
11+
workflow_dispatch:
12+
inputs:
13+
tag:
14+
description: 'tag to (re)build — leave blank to derive `v<version>` from mcpp.toml and create the tag automatically'
15+
required: false
16+
17+
jobs:
18+
build-release:
19+
name: build + upload (linux / x86_64)
20+
runs-on: ubuntu-24.04
21+
permissions:
22+
contents: write # required to create releases + push tags
23+
timeout-minutes: 60
24+
env:
25+
# mcpp resolves MCPP_HOME from the binary's location by default,
26+
# but here we want to share toolchains with the bootstrap sandbox,
27+
# so we pin to a known path.
28+
MCPP_HOME: /home/runner/.mcpp
29+
steps:
30+
# fetch-tags so the resolve-tag step can detect existing tags
31+
# without an extra round-trip; full history isn't needed.
32+
- uses: actions/checkout@v4
33+
with:
34+
fetch-tags: true
35+
36+
- name: Resolve target tag + commit
37+
id: resolve
38+
# Three trigger shapes converge here:
39+
# 1. push: refs/tags/vX.Y.Z → use that tag, build at its commit
40+
# 2. workflow_dispatch with `tag` input set:
41+
# - tag exists on remote → check it out (rebuild scenario)
42+
# - tag doesn't exist → use current HEAD; gh-release
43+
# creates the tag at that commit on upload
44+
# 3. workflow_dispatch with no input → derive `v<version>` from
45+
# mcpp.toml's [package].version, build at current HEAD;
46+
# gh-release creates the tag.
47+
run: |
48+
if [ "${{ github.event_name }}" = "push" ]; then
49+
TAG="${{ github.ref_name }}"
50+
elif [ -n "${{ github.event.inputs.tag }}" ]; then
51+
TAG="${{ github.event.inputs.tag }}"
52+
else
53+
VER=$(awk -F '"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml)
54+
test -n "$VER" || { echo 'failed to read [package].version from mcpp.toml'; exit 1; }
55+
TAG="v$VER"
56+
fi
57+
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
58+
echo "version=${TAG#v}" >> "$GITHUB_OUTPUT"
59+
# If the tag exists on remote AND we're on workflow_dispatch,
60+
# check it out so we rebuild that exact commit. push-tag runs
61+
# already start at the tag commit.
62+
if [ "${{ github.event_name }}" = "workflow_dispatch" ] \
63+
&& git rev-parse --verify "refs/tags/$TAG" >/dev/null 2>&1; then
64+
git checkout --detach "refs/tags/$TAG"
65+
fi
66+
echo "Resolved tag: $TAG (commit $(git rev-parse --short HEAD))"
67+
68+
# Cache mcpp's sandbox: musl-gcc 15.1 + binutils + glibc + linux-headers
69+
# + patchelf + ninja is ~800 MB on disk; without this every release
70+
# rebuilds from cold install. Key on the workspace manifest so a
71+
# toolchain change in mcpp.toml refreshes the cache.
72+
- name: Cache mcpp sandbox
73+
uses: actions/cache@v4
74+
with:
75+
path: ~/.mcpp
76+
key: mcpp-sandbox-${{ runner.os }}-release-${{ hashFiles('mcpp.toml', '.xlings.json') }}
77+
restore-keys: |
78+
mcpp-sandbox-${{ runner.os }}-release-
79+
mcpp-sandbox-${{ runner.os }}-
80+
81+
# Cache xlings + xim:mcpp install.
82+
- name: Cache xlings
83+
uses: actions/cache@v4
84+
with:
85+
path: ~/.xlings
86+
key: xlings-${{ runner.os }}-release-${{ hashFiles('.xlings.json') }}
87+
restore-keys: |
88+
xlings-${{ runner.os }}-release-
89+
xlings-${{ runner.os }}-
90+
91+
- name: Bootstrap mcpp via xlings
92+
env:
93+
XLINGS_NON_INTERACTIVE: '1'
94+
run: |
95+
if [ ! -x "$HOME/.xlings/subos/default/bin/xlings" ]; then
96+
curl -fsSL https://d2learn.org/xlings-install.sh | bash
97+
fi
98+
export PATH="$HOME/.xlings/subos/default/bin:$PATH"
99+
xlings --version
100+
xlings install mcpp -y
101+
MCPP="$HOME/.xlings/subos/default/bin/mcpp"
102+
test -x "$MCPP"
103+
"$MCPP" --version
104+
echo "MCPP=$MCPP" >> "$GITHUB_ENV"
105+
echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV"
106+
107+
- name: Build + pack release artefact (musl static)
108+
id: stage
109+
# Build for the musl-static target, strip the produced ELF, then
110+
# let `mcpp pack` assemble the tarball (binary + top-level wrapper
111+
# + README + LICENSE, contents at archive root). Inject xlings
112+
# afterwards so install.sh consumers get a single self-contained
113+
# bundle.
114+
run: |
115+
TAG="${{ steps.resolve.outputs.tag }}"
116+
VERSION="${{ steps.resolve.outputs.version }}"
117+
TARBALL_NAME="mcpp-${VERSION}-linux-x86_64.tar.gz"
118+
119+
# Build first so we can strip the ELF before pack copies it.
120+
"$MCPP" build --target x86_64-linux-musl
121+
ARTIFACT=$(find target/x86_64-linux-musl -type f -name mcpp | head -1)
122+
test -n "$ARTIFACT"
123+
file "$ARTIFACT" | grep -q 'statically linked'
124+
# Strip — debug info on a static ELF balloons it ~7×.
125+
strip "$ARTIFACT"
126+
127+
# Pack with the freshly-built mcpp (not the bootstrap) so any
128+
# fixes to the pack code path are exercised in the same release
129+
# they ship in. MCPP_HOME is forced so the new binary uses the
130+
# pinned sandbox instead of resolving relative to its own
131+
# location under target/.
132+
MCPP_HOME="$MCPP_HOME" "$ARTIFACT" pack \
133+
--target x86_64-linux-musl \
134+
--mode static \
135+
-o "${TARBALL_NAME}"
136+
137+
# Inject xlings: extract → add bin/xlings to the wrapper dir →
138+
# re-tar preserving the wrapper. The wrapper dir name matches
139+
# the tarball stem (mcpp pack ties the two together).
140+
TARBALL="target/dist/${TARBALL_NAME}"
141+
WRAPPER="${TARBALL_NAME%.tar.gz}"
142+
test -f "$TARBALL"
143+
INJECT=$(mktemp -d)
144+
tar -xzf "$TARBALL" -C "$INJECT"
145+
cp "$XLINGS_BIN" "$INJECT/$WRAPPER/bin/xlings"
146+
chmod +x "$INJECT/$WRAPPER/bin/xlings"
147+
(cd "$INJECT" && tar -czf "$GITHUB_WORKSPACE/${TARBALL}" "$WRAPPER")
148+
rm -rf "$INJECT"
149+
150+
# Stage final dist/ (tarball + sidecars) for upload.
151+
mkdir -p dist
152+
cp "$TARBALL" "dist/${TARBALL_NAME}"
153+
(cd dist && cp "${TARBALL_NAME}" "mcpp-linux-x86_64.tar.gz")
154+
(cd dist && sha256sum "${TARBALL_NAME}" "mcpp-linux-x86_64.tar.gz" > SHA256SUMS)
155+
(cd dist && sha256sum "${TARBALL_NAME}" > "${TARBALL_NAME}.sha256")
156+
(cd dist && sha256sum "mcpp-linux-x86_64.tar.gz" > "mcpp-linux-x86_64.tar.gz.sha256")
157+
158+
# Top-level install.sh — fetched by `curl | bash`.
159+
cp install.sh dist/install.sh
160+
chmod +x dist/install.sh
161+
162+
echo "tag=$TAG" >> $GITHUB_OUTPUT
163+
echo "version=$VERSION" >> $GITHUB_OUTPUT
164+
echo "tarball=${TARBALL_NAME}" >> $GITHUB_OUTPUT
165+
ls -la dist/
166+
167+
- name: Smoke-test the bundled tarball
168+
# Extract to a scratch dir and run mcpp from there with MCPP_HOME
169+
# unset — proves the release artefact is genuinely self-contained.
170+
run: |
171+
VERSION="${{ steps.stage.outputs.version }}"
172+
TARBALL_NAME="${{ steps.stage.outputs.tarball }}"
173+
# Wrapper dir inside the tarball matches its stem (mcpp pack
174+
# ties the two together).
175+
WRAPPER="${TARBALL_NAME%.tar.gz}"
176+
SMOKE=$(mktemp -d)
177+
tar -xzf "dist/${TARBALL_NAME}" -C "$SMOKE"
178+
ROOT="$SMOKE/$WRAPPER"
179+
test -x "$ROOT/bin/mcpp"
180+
test -x "$ROOT/bin/xlings"
181+
test -x "$ROOT/mcpp"
182+
file "$ROOT/bin/mcpp" | grep -q 'statically linked'
183+
env -u MCPP_HOME "$ROOT/bin/mcpp" --version
184+
env -u MCPP_HOME "$ROOT/bin/mcpp" --help | head -10
185+
# Top-level wrapper reports the same version we're shipping.
186+
env -u MCPP_HOME "$ROOT/mcpp" --version | grep -q "$VERSION"
187+
# MCPP_HOME should auto-resolve to the extracted root.
188+
out=$(env -u MCPP_HOME "$ROOT/bin/mcpp" self env)
189+
echo "$out" | grep -q "MCPP_HOME *= *$ROOT"
190+
191+
- name: Generate source tarball + xpkg.lua via mcpp publish
192+
# Use the freshly-built mcpp to produce the source tarball + xpkg
193+
# descriptor for mcpp-index. The release tarball wraps its
194+
# contents in a `<tarball-stem>/` directory so the extract path
195+
# is $PUB/$WRAPPER/bin/mcpp.
196+
run: |
197+
VERSION="${{ steps.stage.outputs.version }}"
198+
TARBALL_NAME="${{ steps.stage.outputs.tarball }}"
199+
WRAPPER="${TARBALL_NAME%.tar.gz}"
200+
PUB=$(mktemp -d)
201+
tar -xzf "dist/${TARBALL_NAME}" -C "$PUB"
202+
MCPP_BIN="$PUB/$WRAPPER/bin/mcpp"
203+
env -u MCPP_HOME "$MCPP_BIN" publish --dry-run --allow-dirty
204+
test -f "target/dist/mcpp-${VERSION}.tar.gz"
205+
test -f "target/dist/mcpp.lua"
206+
cp "target/dist/mcpp-${VERSION}.tar.gz" dist/
207+
cp "target/dist/mcpp.lua" dist/
208+
ls -la dist/
209+
210+
- name: Extract release notes from CHANGELOG
211+
id: notes
212+
run: |
213+
TAG="${{ steps.stage.outputs.tag }}"
214+
VERSION="${{ steps.stage.outputs.version }}"
215+
awk -v v="$VERSION" '
216+
/^## \[/ {
217+
if (in_section) exit
218+
if ($0 ~ "\\[" v "\\]") { in_section=1; next }
219+
}
220+
in_section { print }
221+
' CHANGELOG.md > dist/RELEASE_NOTES.md || true
222+
if [ ! -s dist/RELEASE_NOTES.md ]; then
223+
echo "(no CHANGELOG entry found for $VERSION)" > dist/RELEASE_NOTES.md
224+
fi
225+
echo "--- RELEASE_NOTES.md ---"
226+
cat dist/RELEASE_NOTES.md
227+
228+
- name: Create GitHub Release
229+
uses: softprops/action-gh-release@v2
230+
with:
231+
tag_name: ${{ steps.stage.outputs.tag }}
232+
name: ${{ steps.stage.outputs.tag }}
233+
body_path: dist/RELEASE_NOTES.md
234+
draft: false
235+
prerelease: false
236+
files: |
237+
dist/mcpp-${{ steps.stage.outputs.version }}-linux-x86_64.tar.gz
238+
dist/mcpp-${{ steps.stage.outputs.version }}-linux-x86_64.tar.gz.sha256
239+
dist/mcpp-linux-x86_64.tar.gz
240+
dist/mcpp-linux-x86_64.tar.gz.sha256
241+
dist/install.sh
242+
dist/SHA256SUMS
243+
dist/mcpp-${{ steps.stage.outputs.version }}.tar.gz
244+
dist/mcpp.lua

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# mcpp's own build output (mcpp build / mcpp pack)
2+
target/
3+
4+
# mcpp's per-workspace xlings sandbox + lockfile + diagnostic logs
5+
/.xlings/
6+
.claude
7+
mcpp.lock
8+
doctor.log
9+
10+
# editor / OS
11+
.DS_Store
12+
*.swp
13+
*.swo
14+
.vscode/
15+
.idea/
16+
17+
# generated module artifacts
18+
*.gcm
19+
*.pcm
20+
*.ifc
21+
*.ddi

.xlings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"workspace": {
3+
"mcpp": "0.0.1"
4+
}
5+
}

0 commit comments

Comments
 (0)