GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — mcpp/.github/workflows/release.yml at main · mcpp-community/mcpp 996 lines (927 loc) · 47 KB / Copy pathrelease.ymlFile metadata and controls996 lines (927 loc) · 47 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 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 name: release # Self-host release: bootstrap mcpp from xlings (xim:mcpp), build the # musl-static artefact via `mcpp pack --target x86_64-linux-musl -o ...`, # inject xlings into the produced tarball for install.sh consumers, # smoke-test, upload. on: push: tags: [ 'v*' ] workflow_dispatch: inputs: tag: description: 'tag to (re)build — leave blank to derive `v<version>` from mcpp.toml and create the tag automatically' required: false jobs: # A RELEASE IS GATED BY THE ECOSYSTEM'S OWN PROJECTS (the 2026-09-28 design, # WS10): they are built with the candidate first # (.github/release-canaries.toml), and the job that creates the tag needs # them. A downstream project validates the release in its own pull request. canaries: uses: ./.github/workflows/release-canaries.yml build-release: name: build + upload (linux / x86_64) needs: canaries runs-on: ubuntu-24.04 permissions: contents: write # required to create releases + push tags timeout-minutes: 60 env: # mcpp resolves MCPP_HOME from the binary's location by default, # but here we want to share toolchains with the bootstrap sandbox, # so we pin to a known path. MCPP_HOME: /home/runner/.mcpp steps: # fetch-depth: 0 instead of fetch-tags: true — actions/checkout@v4 # fails on push-tag triggers when both the ref'd tag and # `fetch-tags: true` are set: # "Cannot fetch both <sha> and refs/tags/vX.Y.Z to refs/tags/vX.Y.Z" # Full-history fetch covers the resolve-tag step's needs without # that contention. - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Resolve target tag + commit id: resolve # Three trigger shapes converge here: # 1. push: refs/tags/vX.Y.Z → use that tag, build at its commit # 2. workflow_dispatch with `tag` input set: # - tag exists on remote → check it out (rebuild scenario) # - tag doesn't exist → use current HEAD; gh-release # creates the tag at that commit on upload # 3. workflow_dispatch with no input → derive `v<version>` from # mcpp.toml's [package].version, build at current HEAD; # gh-release creates the tag. run: | if [ "${{ github.event_name }}" = "push" ]; then TAG="${{ github.ref_name }}" elif [ -n "${{ github.event.inputs.tag }}" ]; then TAG="${{ github.event.inputs.tag }}" else VER=$(awk -F '"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml) test -n "$VER" || { echo 'failed to read [package].version from mcpp.toml'; exit 1; } TAG="v$VER" fi echo "tag=$TAG" >> "$GITHUB_OUTPUT" echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" # If the tag exists on remote AND we're on workflow_dispatch, # check it out so we rebuild that exact commit. push-tag runs # already start at the tag commit. if [ "${{ github.event_name }}" = "workflow_dispatch" ] \ && git rev-parse --verify "refs/tags/$TAG" >/dev/null 2>&1; then git checkout --detach "refs/tags/$TAG" fi echo "Resolved tag: $TAG (commit $(git rev-parse --short HEAD))" # Cache mcpp's sandbox: musl-gcc 15.1 + binutils + glibc + linux-headers # + patchelf + ninja is ~800 MB on disk; without this every release # rebuilds from cold install. Key on the workspace manifest so a # toolchain change in mcpp.toml refreshes the cache. - name: Cache mcpp sandbox uses: actions/cache@v4 with: path: ~/.mcpp key: mcpp-sandbox-${{ runner.os }}-release-${{ hashFiles('mcpp.toml', '.xlings.json') }} restore-keys: | mcpp-sandbox-${{ runner.os }}-release- # Cache xlings + xim:mcpp install. - name: Cache xlings uses: actions/cache@v4 with: path: ~/.xlings key: xlings-${{ runner.os }}-release-xl0462-${{ hashFiles('.xlings.json') }} restore-keys: | xlings-${{ runner.os }}-release-xl0462- - name: Bootstrap mcpp via xlings env: XLINGS_NON_INTERACTIVE: '1' # Pin xlings to a known-good version. The upstream install # script always grabs `latest` (no version override), so we # download + self-install manually to avoid broken releases. XLINGS_VERSION: '2026.10.26.1' run: | # BY VERSION, NOT BY PRESENCE. A restored ~/.xlings holds the # xlings of an earlier release, and this binary is the one the # tarball bundles: 2026.10.10.1 shipped xlings 2026.10.8.1 on # linux-x86_64 because one was present. have=$("$HOME/.xlings/subos/default/bin/xlings" --version 2>/dev/null | awk '{print $2}') if [ "$have" != "$XLINGS_VERSION" ]; then tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" bash "$GITHUB_WORKSPACE/.github/tools/fetch_release.sh" \ "https://github.com/openxlings/xlings/releases/download/v${XLINGS_VERSION}/${tarball}" \ "/tmp/${tarball}" tar -xzf "/tmp/${tarball}" -C /tmp "/tmp/xlings-${XLINGS_VERSION}-linux-x86_64/subos/default/bin/xlings" self install fi export PATH="$HOME/.xlings/subos/default/bin:$PATH" xlings --version test "$(xlings --version | awk '{print $2}')" = "$XLINGS_VERSION" || { echo "::error::the xlings this release bundles is $(xlings --version), not $XLINGS_VERSION"; exit 1; } # Pinned to .xlings.json — a bare `xlings install mcpp` resolves # "newest in this runner's index copy" and put 0.0.105 (below the # index floor) into this job. See .github/tools/install_pinned_mcpp.sh. MCPP=$(bash "$GITHUB_WORKSPACE/.github/tools/install_pinned_mcpp.sh" "$GITHUB_WORKSPACE") echo "MCPP=$MCPP" >> "$GITHUB_ENV" echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV" - name: Build + pack release artefact (musl static) id: stage # Build for the musl-static target, strip the produced ELF, then # let `mcpp pack` assemble the tarball (binary + top-level wrapper # + README + LICENSE, contents at archive root). Inject xlings # afterwards so install.sh consumers get a single self-contained # bundle. run: | TAG="${{ steps.resolve.outputs.tag }}" VERSION="${{ steps.resolve.outputs.version }}" TARBALL_NAME="mcpp-${VERSION}-linux-x86_64.tar.gz" # Build first so we can strip the ELF before pack copies it. export MCPP_VENDORED_XLINGS="$XLINGS_BIN" "$MCPP" build --target x86_64-linux-musl ARTIFACT=$(find target/x86_64-linux-musl -type f -name mcpp | head -1) test -n "$ARTIFACT" file "$ARTIFACT" | grep -q 'statically linked' # NB: stripping here is pointless — `mcpp pack` below rebuilds the # binary and overwrites it, which is why every release up to and # including 2026.7.28.2 shipped an UNSTRIPPED bin/mcpp despite the # strip that used to live on this line. The payload is slimmed after # packing instead, in the inject step below. # Pack with the freshly-built mcpp (not the bootstrap) so any # fixes to the pack code path are exercised in the same release # they ship in. MCPP_HOME is forced so the new binary uses the # pinned sandbox instead of resolving relative to its own # location under target/. MCPP_HOME="$MCPP_HOME" "$ARTIFACT" pack \ --target x86_64-linux-musl \ --mode static \ -o "${TARBALL_NAME}" # Inject xlings: extract → add registry/bin/xlings to the wrapper # dir → re-tar preserving the wrapper. Since 0.0.4 the bundled # xlings lives at <ROOT>/registry/bin/xlings (= <XLINGS_HOME>/bin/xlings). TARBALL="target/dist/${TARBALL_NAME}" WRAPPER="${TARBALL_NAME%.tar.gz}" test -f "$TARBALL" INJECT=$(mktemp -d) tar -xzf "$TARBALL" -C "$INJECT" mkdir -p "$INJECT/$WRAPPER/registry/bin" cp "$XLINGS_BIN" "$INJECT/$WRAPPER/registry/bin/xlings" chmod +x "$INJECT/$WRAPPER/registry/bin/xlings" # Slim AFTER pack (pack rebuilds bin/mcpp) and BEFORE tar. Asserts # the result, so a strip that silently stops working fails the # release instead of quietly shipping a 34.8MB tarball again. bash .github/tools/slim_linux_payload.sh "$INJECT/$WRAPPER" (cd "$INJECT" && tar -czf "$GITHUB_WORKSPACE/${TARBALL}" "$WRAPPER") rm -rf "$INJECT" # Stage final dist/ (tarball + sidecars) for upload. mkdir -p dist cp "$TARBALL" "dist/${TARBALL_NAME}" (cd dist && cp "${TARBALL_NAME}" "mcpp-linux-x86_64.tar.gz") (cd dist && sha256sum "${TARBALL_NAME}" "mcpp-linux-x86_64.tar.gz" > SHA256SUMS) (cd dist && sha256sum "${TARBALL_NAME}" > "${TARBALL_NAME}.sha256") (cd dist && sha256sum "mcpp-linux-x86_64.tar.gz" > "mcpp-linux-x86_64.tar.gz.sha256") # Top-level install.sh — fetched by `curl | bash`. cp install.sh dist/install.sh chmod +x dist/install.sh echo "tag=$TAG" >> $GITHUB_OUTPUT echo "version=$VERSION" >> $GITHUB_OUTPUT echo "tarball=${TARBALL_NAME}" >> $GITHUB_OUTPUT ls -la dist/ - name: Smoke-test the bundled tarball # Extract to a scratch dir and run mcpp from there with MCPP_HOME # unset — proves the release artefact is genuinely self-contained. run: | VERSION="${{ steps.stage.outputs.version }}" TARBALL_NAME="${{ steps.stage.outputs.tarball }}" # Wrapper dir inside the tarball matches its stem (mcpp pack # ties the two together). WRAPPER="${TARBALL_NAME%.tar.gz}" SMOKE=$(mktemp -d) tar -xzf "dist/${TARBALL_NAME}" -C "$SMOKE" ROOT="$SMOKE/$WRAPPER" test -x "$ROOT/bin/mcpp" test -x "$ROOT/registry/bin/xlings" test -x "$ROOT/mcpp" file "$ROOT/bin/mcpp" | grep -q 'statically linked' env -u MCPP_HOME "$ROOT/bin/mcpp" --version env -u MCPP_HOME "$ROOT/bin/mcpp" --help | head -10 # Top-level wrapper reports the same version we're shipping. env -u MCPP_HOME "$ROOT/mcpp" --version | grep -q "$VERSION" # MCPP_HOME should auto-resolve to the extracted root. out=$(env -u MCPP_HOME "$ROOT/bin/mcpp" self env) # R4 (mcpp#832): the binary pack rebuilt with this release's own # engine trims the paths it carries. bash .github/tools/check_release_paths.sh "$ROOT/bin/mcpp" echo "$out" | grep -q "MCPP_HOME *= *$ROOT" - name: Generate source tarball + xpkg.lua via mcpp publish # Use the freshly-built mcpp to produce the source tarball + xpkg # descriptor for mcpp-index. The release tarball wraps its # contents in a `<tarball-stem>/` directory so the extract path # is $PUB/$WRAPPER/bin/mcpp. run: | VERSION="${{ steps.stage.outputs.version }}" TARBALL_NAME="${{ steps.stage.outputs.tarball }}" WRAPPER="${TARBALL_NAME%.tar.gz}" PUB=$(mktemp -d) tar -xzf "dist/${TARBALL_NAME}" -C "$PUB" MCPP_BIN="$PUB/$WRAPPER/bin/mcpp" env -u MCPP_HOME "$MCPP_BIN" publish --dry-run --allow-dirty test -f "target/dist/mcpp-${VERSION}.tar.gz" test -f "target/dist/mcpp.lua" cp "target/dist/mcpp-${VERSION}.tar.gz" dist/ cp "target/dist/mcpp.lua" dist/ ls -la dist/ - name: Extract release notes from CHANGELOG id: notes run: | TAG="${{ steps.stage.outputs.tag }}" VERSION="${{ steps.stage.outputs.version }}" awk -v v="$VERSION" ' /^## \[/ { if (in_section) exit if ($0 ~ "\\[" v "\\]") { in_section=1; next } } in_section { print } ' CHANGELOG.md > dist/RELEASE_NOTES.md || true # A RELEASE IS OUTWARD-FACING AND PERMANENT: publishing one whose # notes silently came out empty is not a lesser failure than any # other defect this workflow guards against, and until now it # produced none — v2026.9.18.1 went out with its notes body reading # literally "(no CHANGELOG entry found for 2026.9.18.1)", because # CHANGELOG.md had no matching `## [<version>]` heading and this # step wrote a placeholder and moved on rather than stopping. Stop # instead: no matching section is an authoring mistake (the # section is missing, or its version string does not match what is # being tagged), and the fix belongs in CHANGELOG.md before the # release is retried, not in a release whose notes nobody reads. if [ ! -s dist/RELEASE_NOTES.md ]; then echo "::error::CHANGELOG.md has no '## [$VERSION]' section — refusing to publish a release with empty notes. Add that section (docs/92-release.md) and re-run, rather than let this placeholder ship: (no CHANGELOG entry found for $VERSION)" exit 1 fi echo "--- RELEASE_NOTES.md ---" cat dist/RELEASE_NOTES.md - name: Create GitHub Release uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.stage.outputs.tag }} name: ${{ steps.stage.outputs.tag }} body_path: dist/RELEASE_NOTES.md draft: false prerelease: false files: | dist/mcpp-${{ steps.stage.outputs.version }}-linux-x86_64.tar.gz dist/mcpp-${{ steps.stage.outputs.version }}-linux-x86_64.tar.gz.sha256 dist/mcpp-linux-x86_64.tar.gz dist/mcpp-linux-x86_64.tar.gz.sha256 dist/install.sh dist/SHA256SUMS dist/mcpp-${{ steps.stage.outputs.version }}.tar.gz dist/mcpp.lua build-linux-aarch64: name: build (linux / aarch64, cross) runs-on: ubuntu-24.04 needs: build-release permissions: contents: write timeout-minutes: 70 steps: - uses: actions/checkout@v4 - name: Install system deps + qemu run: | # The runner image carries third-party apt lists (Google Chrome # among them) that this job does not use, and a transient # `Hash Sum mismatch` on one of them fails the whole update -- which # killed two cross-build jobs in setup, before a single byte was # compiled. Dropping the lists this job has no use for is what makes # the step's failure mean something about this job. # # BY CONTENT, NOT BY FILENAME. The first attempt removed # `google-chrome.list` and the update failed on the same URL: on # ubuntu-24.04 the runner writes deb822 `.sources` files, so the # name was a guess and the guess was wrong. sudo grep -rlE 'dl[.]google[.]com|packages[.]microsoft[.]com' \ /etc/apt/sources.list.d/ 2>/dev/null | xargs -r sudo rm -f sudo apt-get update -qq sudo apt-get install -y curl git build-essential qemu-user-static qemu-aarch64-static --version | head -1 - name: Resolve tag + version id: resolve run: | VERSION=$(grep -E '^version' mcpp.toml | head -1 | sed 's/.*"\([^"]*\)".*/\1/') echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "tag=v$VERSION" >> "$GITHUB_OUTPUT" - name: Bootstrap mcpp via xlings env: XLINGS_NON_INTERACTIVE: '1' XLINGS_VERSION: '2026.10.26.1' run: | tarball="xlings-${XLINGS_VERSION}-linux-x86_64.tar.gz" bash "$GITHUB_WORKSPACE/.github/tools/fetch_release.sh" \ "https://github.com/openxlings/xlings/releases/download/v${XLINGS_VERSION}/${tarball}" \ "/tmp/${tarball}" tar -xzf "/tmp/${tarball}" -C /tmp "/tmp/xlings-${XLINGS_VERSION}-linux-x86_64/subos/default/bin/xlings" self install echo "$HOME/.xlings/subos/current/bin" >> "$GITHUB_PATH" echo "$HOME/.xlings/bin" >> "$GITHUB_PATH" echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV" - name: Bootstrap mcpp + refresh index (latest, GLOBAL) run: | xlings config --mirror GLOBAL 2>/dev/null || true xlings update -y 2>/dev/null || xlings update 2>/dev/null || true MCPP=$(bash "$GITHUB_WORKSPACE/.github/tools/install_pinned_mcpp.sh" "$GITHUB_WORKSPACE") echo "MCPP=$MCPP" >> "$GITHUB_ENV" "$MCPP" --version - name: Cross-build mcpp -> aarch64-linux-musl (this release's source) run: | export MCPP_VENDORED_XLINGS="$XLINGS_BIN" # "$MCPP", not a bare `mcpp`: the bare form runs the PATH shim, which # resolves to whatever version xvm has selected — so pinning the # bootstrap would have looked correct and changed nothing here. "$MCPP" self config --mirror GLOBAL 2>/dev/null || true # The published bootstrap mcpp predates aarch64 cross-build support # (the feature landed after the last release), so it resolves the # x86_64 host musl-gcc for an aarch64 target. Two-stage instead: build # THIS release's x86_64 mcpp first, then cross-build aarch64 with it. "$MCPP" build --target x86_64-linux-musl FRESH=$(find target/x86_64-linux-musl -type f -name mcpp | head -1) test -n "$FRESH" "$FRESH" build --target aarch64-linux-musl BIN=$(find target/aarch64-linux-musl -type f -name mcpp | head -1) test -n "$BIN" file "$BIN" | grep -q 'ARM aarch64' file "$BIN" | grep -q 'statically linked' echo "MCPP_AARCH64=$GITHUB_WORKSPACE/$BIN" >> "$GITHUB_ENV" - name: Package aarch64 release (+ bundle aarch64 xlings) id: stage run: | VERSION="${{ steps.resolve.outputs.version }}" TARBALL_NAME="mcpp-${VERSION}-linux-aarch64.tar.gz" WRAPPER="mcpp-${VERSION}-linux-aarch64" STAGING=$(mktemp -d) mkdir -p "$STAGING/$WRAPPER/bin" cp "$MCPP_AARCH64" "$STAGING/$WRAPPER/bin/mcpp" # The binary is aarch64, so the host x86_64 `strip` cannot touch it — # resolve a cross-capable one. Required, not best-effort: the old # `|| true` here meant a missing tool silently shipped a fat tarball. # (slim_linux_payload.sh runs after xlings is staged, below.) STRIP=$(find "$HOME/.mcpp" -name 'aarch64-linux-musl-strip' -type f 2>/dev/null | head -1) [ -n "$STRIP" ] || STRIP=$(command -v llvm-strip 2>/dev/null || true) [ -n "$STRIP" ] || STRIP=$(command -v aarch64-linux-gnu-strip 2>/dev/null || true) [ -n "$STRIP" ] || { echo "no aarch64-capable strip found"; exit 1; } echo "aarch64 strip: $STRIP" cp LICENSE "$STAGING/$WRAPPER/" 2>/dev/null || true cp README.md "$STAGING/$WRAPPER/" 2>/dev/null || true cat > "$STAGING/$WRAPPER/mcpp" << 'LAUNCHER' #!/bin/sh exec "$(dirname "$0")/bin/mcpp" "$@" LAUNCHER chmod +x "$STAGING/$WRAPPER/mcpp" # Bundle the aarch64 xlings so install.sh consumers on aarch64 get an # aarch64 xlings, not the x86_64 bootstrap one. The three literals # below are pinned to the same version as XLINGS_VERSION; they are # NOT interpolated from it, so check_version_pins.sh scans for them # explicitly (they were absent from the old lock-step comment). XLA="xlings-2026.10.26.1-linux-aarch64.tar.gz" # NOT fetch_release.sh: this asset is OPTIONAL and the `if` is the # point — an arch with no prebuilt xlings must fall through quietly, # while the helper retries a 404 five times before giving up. The one # flag that matters here is --retry-all-errors: `curl: (52) Empty # reply from server` is a transport error, so plain --retry does not # cover it. if curl -fsSL --retry 3 --retry-delay 2 --retry-all-errors \ --connect-timeout 20 --max-time 600 -o "/tmp/$XLA" \ "https://github.com/openxlings/xlings/releases/download/v2026.10.26.1/$XLA"; then tar -xzf "/tmp/$XLA" -C /tmp XLBIN=$(find /tmp/xlings-2026.10.26.1-linux-aarch64 -path '*/bin/xlings' -type f | head -1) if [ -n "$XLBIN" ]; then mkdir -p "$STAGING/$WRAPPER/registry/bin" cp "$XLBIN" "$STAGING/$WRAPPER/registry/bin/xlings" chmod +x "$STAGING/$WRAPPER/registry/bin/xlings" fi fi # Slim both shipped ELFs with the cross strip resolved above, and # assert the result (the vendored xlings was 86.9MB unstripped here). bash .github/tools/slim_linux_payload.sh "$STAGING/$WRAPPER" "$STRIP" mkdir -p dist (cd "$STAGING" && tar -czf "$GITHUB_WORKSPACE/dist/${TARBALL_NAME}" "$WRAPPER") cp "dist/${TARBALL_NAME}" "dist/mcpp-linux-aarch64.tar.gz" (cd dist && sha256sum "${TARBALL_NAME}" > "${TARBALL_NAME}.sha256") (cd dist && sha256sum "mcpp-linux-aarch64.tar.gz" > "mcpp-linux-aarch64.tar.gz.sha256") echo "tarball=${TARBALL_NAME}" >> "$GITHUB_OUTPUT" ls -la dist/ - name: Smoke-test the aarch64 tarball (qemu) run: | TARBALL_NAME="${{ steps.stage.outputs.tarball }}" WRAPPER="${TARBALL_NAME%.tar.gz}" SMOKE=$(mktemp -d) tar -xzf "dist/${TARBALL_NAME}" -C "$SMOKE" ver=$(qemu-aarch64-static "$SMOKE/$WRAPPER/bin/mcpp" --version) echo "$ver"; echo "$ver" | grep -q 'mcpp' - name: Upload aarch64 artifacts to release uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.resolve.outputs.tag }} files: | dist/mcpp-${{ steps.resolve.outputs.version }}-linux-aarch64.tar.gz dist/mcpp-${{ steps.resolve.outputs.version }}-linux-aarch64.tar.gz.sha256 dist/mcpp-linux-aarch64.tar.gz dist/mcpp-linux-aarch64.tar.gz.sha256 build-macos: name: build (macOS / ARM64) runs-on: macos-15 needs: build-release permissions: contents: write timeout-minutes: 30 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Resolve tag id: resolve run: | if [ "${{ github.event_name }}" = "push" ]; then TAG="${{ github.ref_name }}" elif [ -n "${{ github.event.inputs.tag }}" ]; then TAG="${{ github.event.inputs.tag }}" else VER=$(awk -F '"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml) TAG="v$VER" fi echo "tag=$TAG" >> "$GITHUB_OUTPUT" echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" if [ "${{ github.event_name }}" = "workflow_dispatch" ] \ && git rev-parse --verify "refs/tags/$TAG" >/dev/null 2>&1; then git checkout --detach "refs/tags/$TAG" fi - name: Cache xlings uses: actions/cache@v4 with: path: ~/.xlings key: xlings-macos15-release-xl0462-${{ hashFiles('.xlings.json') }} restore-keys: | xlings-macos15-release-xl0462- - name: Bootstrap mcpp via xlings env: XLINGS_NON_INTERACTIVE: '1' XLINGS_VERSION: '2026.10.26.1' run: | # By version, not by presence (see the Linux job). have=$("$HOME/.xlings/subos/default/bin/xlings" --version 2>/dev/null | awk '{print $2}') if [ "$have" != "$XLINGS_VERSION" ]; then WORK=$(mktemp -d) tarball="xlings-${XLINGS_VERSION}-macosx-arm64.tar.gz" bash "$GITHUB_WORKSPACE/.github/tools/fetch_release.sh" \ "https://github.com/openxlings/xlings/releases/download/v${XLINGS_VERSION}/${tarball}" \ "${WORK}/${tarball}" tar -xzf "${WORK}/${tarball}" -C "${WORK}" "${WORK}/xlings-${XLINGS_VERSION}-macosx-arm64/subos/default/bin/xlings" self install fi export PATH="$HOME/.xlings/subos/default/bin:$PATH" xlings --version test "$(xlings --version | awk '{print $2}')" = "$XLINGS_VERSION" || { echo "::error::the xlings this release bundles is $(xlings --version), not $XLINGS_VERSION"; exit 1; } # Pinned to .xlings.json — a bare `xlings install mcpp` resolves # "newest in this runner's index copy" and put 0.0.105 (below the # index floor) into this job. See .github/tools/install_pinned_mcpp.sh. MCPP=$(bash "$GITHUB_WORKSPACE/.github/tools/install_pinned_mcpp.sh" "$GITHUB_WORKSPACE") echo "MCPP=$MCPP" >> "$GITHUB_ENV" echo "XLINGS_BIN=$HOME/.xlings/subos/default/bin/xlings" >> "$GITHUB_ENV" - name: Build mcpp from source (two-stage self-host) env: # macOS min-version support: target macOS 14 so the release runs # on 14.0+ instead of only the runner's OS (the official LLVM # static libc++ archives are built for macOS 14 — going lower # needs a custom libc++ build, tracked as follow-up). Needs # static LLVM libc++ — the system libc++ on older macOS lacks # LLVM-20-era C++23 symbols (std::print's __is_posix_terminal # etc.; minos-14 + dynamic libc++ dies at launch on macos-14 CI). # See xlings .agents/docs/2026-06-05-macos-min-version-support.md. MACOSX_DEPLOYMENT_TARGET: '14.0' run: | export PATH="$HOME/.xlings/subos/default/bin:$PATH" export MCPP_VENDORED_XLINGS="$XLINGS_BIN" # Stage 1: the bootstrap mcpp builds this release's source. The # bootstrap's macOS link path predates the staticStdlib # implementation (hardcoded -lc++), so stage 1 links the system # libc++ — fine, it only needs to RUN on this runner. "$MCPP" build STAGE1=$(find target -path "*/bin/mcpp" | head -1) STAGE1=$(cd "$(dirname "$STAGE1")" && pwd)/$(basename "$STAGE1") # --no-cache removes target/, including the stage-1 compiler. Ninja # invokes that compiler again for dependency scanning, so keep the # bootstrap outside the tree the second stage is about to clear. STAGE1_COPY=$(mktemp -d "$RUNNER_TEMP/mcpp-stage1.XXXXXX")/mcpp cp "$STAGE1" "$STAGE1_COPY" STAGE1="$STAGE1_COPY" "$STAGE1" --version # Stage 2: this release's mcpp rebuilds itself — flags.cppm's # native staticStdlib link produces the static minos-14 binary. # NOTE: stage 2 lands in a NEW fingerprint directory (its # fingerprint includes the deployment target; the bootstrap's # did not) — pick the most recently modified binary, not the # first find hit. "$STAGE1" build --no-cache MCPP_BIN=$(ls -t $(find target -path "*/bin/mcpp" -type f) | head -1) MCPP_BIN=$(cd "$(dirname "$MCPP_BIN")" && pwd)/$(basename "$MCPP_BIN") test -x "$MCPP_BIN" file "$MCPP_BIN" otool -L "$MCPP_BIN" echo "=== LC_BUILD_VERSION (must be minos 14.0) ===" otool -l "$MCPP_BIN" | grep -A4 LC_BUILD_VERSION | head -6 otool -l "$MCPP_BIN" | grep -A4 LC_BUILD_VERSION | grep -q "minos 14.0" \ || { echo "FAIL: expected minos 14.0"; exit 1; } if otool -L "$MCPP_BIN" | grep -q "libc++"; then echo "FAIL: still linked against system libc++"; exit 1 fi "$MCPP_BIN" --version echo "MCPP_BIN=$MCPP_BIN" >> "$GITHUB_ENV" - name: Package macOS release id: stage run: | VERSION="${{ steps.resolve.outputs.version }}" TARBALL_NAME="mcpp-${VERSION}-macosx-arm64.tar.gz" WRAPPER="mcpp-${VERSION}-macosx-arm64" # Create release layout STAGING=$(mktemp -d) mkdir -p "$STAGING/$WRAPPER/bin" cp "$MCPP_BIN" "$STAGING/$WRAPPER/bin/mcpp" # Strip (Mach-O) strip "$STAGING/$WRAPPER/bin/mcpp" 2>/dev/null || true # Copy metadata cp LICENSE "$STAGING/$WRAPPER/" 2>/dev/null || true cp README.md "$STAGING/$WRAPPER/" 2>/dev/null || true # Shell launcher (same as Linux) cat > "$STAGING/$WRAPPER/mcpp" << 'LAUNCHER' #!/bin/sh exec "$(dirname "$0")/bin/mcpp" "$@" LAUNCHER chmod +x "$STAGING/$WRAPPER/mcpp" # Bundle xlings for install.sh consumers XLINGS_BIN="$HOME/.xlings/subos/default/bin/xlings" if [ -x "$XLINGS_BIN" ]; then mkdir -p "$STAGING/$WRAPPER/registry/bin" cp "$XLINGS_BIN" "$STAGING/$WRAPPER/registry/bin/xlings" chmod +x "$STAGING/$WRAPPER/registry/bin/xlings" fi # Create tarball mkdir -p dist (cd "$STAGING" && tar -czf "$GITHUB_WORKSPACE/dist/${TARBALL_NAME}" "$WRAPPER") # Versionless alias cp "dist/${TARBALL_NAME}" "dist/mcpp-macosx-arm64.tar.gz" # SHA256 (cd dist && shasum -a 256 "${TARBALL_NAME}" > "${TARBALL_NAME}.sha256") (cd dist && shasum -a 256 "mcpp-macosx-arm64.tar.gz" > "mcpp-macosx-arm64.tar.gz.sha256") echo "tarball=${TARBALL_NAME}" >> "$GITHUB_OUTPUT" ls -la dist/ - name: Smoke-test the tarball run: | VERSION="${{ steps.resolve.outputs.version }}" TARBALL_NAME="${{ steps.stage.outputs.tarball }}" WRAPPER="${TARBALL_NAME%.tar.gz}" SMOKE=$(mktemp -d) tar -xzf "dist/${TARBALL_NAME}" -C "$SMOKE" "$SMOKE/$WRAPPER/bin/mcpp" --version "$SMOKE/$WRAPPER/mcpp" --version | grep -q "$VERSION" # R4 (mcpp#832): stage 2 is this release's own engine, and it trims # the paths the binary carries. bash .github/tools/check_release_paths.sh "$SMOKE/$WRAPPER/bin/mcpp" - name: Upload macOS artifacts to release uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.resolve.outputs.tag }} files: | dist/mcpp-${{ steps.resolve.outputs.version }}-macosx-arm64.tar.gz dist/mcpp-${{ steps.resolve.outputs.version }}-macosx-arm64.tar.gz.sha256 dist/mcpp-macosx-arm64.tar.gz dist/mcpp-macosx-arm64.tar.gz.sha256 build-windows: name: build (Windows / x86_64) runs-on: windows-latest needs: build-release permissions: contents: write timeout-minutes: 45 env: MCPP_HOME: C:\Users\runneradmin\.mcpp steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Resolve tag id: resolve shell: bash run: | if [ "${{ github.event_name }}" = "push" ]; then TAG="${{ github.ref_name }}" elif [ -n "${{ github.event.inputs.tag }}" ]; then TAG="${{ github.event.inputs.tag }}" else VER=$(awk -F '"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml) TAG="v$VER" fi echo "tag=$TAG" >> "$GITHUB_OUTPUT" echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" if [ "${{ github.event_name }}" = "workflow_dispatch" ] \ && git rev-parse --verify "refs/tags/$TAG" >/dev/null 2>&1; then git checkout --detach "refs/tags/$TAG" fi - name: Cache mcpp sandbox uses: actions/cache@v4 with: path: ~\.mcpp key: mcpp-sandbox-${{ runner.os }}-release-${{ hashFiles('mcpp.toml', '.xlings.json') }} restore-keys: | mcpp-sandbox-${{ runner.os }}-release- - name: Cache xlings uses: actions/cache@v4 with: path: ~\.xlings key: xlings-${{ runner.os }}-release-xl0462-${{ hashFiles('.xlings.json') }} restore-keys: | xlings-${{ runner.os }}-release-xl0462- - name: Bootstrap mcpp via xlings shell: bash env: XLINGS_NON_INTERACTIVE: '1' XLINGS_VERSION: '2026.10.26.1' run: | # Captured before the `cd` below, in POSIX form: this step never # returns to the workspace, and GITHUB_WORKSPACE is a backslash # Windows path that git-bash tools mangle. REPO_DIR="$(pwd)" WORK=$(mktemp -d) zipfile="xlings-${XLINGS_VERSION}-windows-x86_64.zip" bash "$GITHUB_WORKSPACE/.github/tools/fetch_release.sh" \ "https://github.com/openxlings/xlings/releases/download/v${XLINGS_VERSION}/${zipfile}" \ "${WORK}/${zipfile}" cd "${WORK}" unzip -q "${zipfile}" "$WORK/xlings-${XLINGS_VERSION}-windows-x86_64/subos/default/bin/xlings.exe" self install export PATH="$USERPROFILE/.xlings/subos/default/bin:$PATH" echo "$USERPROFILE/.xlings/subos/default/bin" >> "$GITHUB_PATH" xlings.exe --version test "$(xlings.exe --version | awk '{print $2}' | tr -d '\r')" = "$XLINGS_VERSION" || { echo "::error::the xlings this release bundles is not $XLINGS_VERSION"; exit 1; } # Pinned + version-scoped lookup. The old `find | head -1` returned # whichever version the directory walk reached first. MCPP=$(bash "$REPO_DIR/.github/tools/install_pinned_mcpp.sh" "$REPO_DIR") echo "MCPP=$MCPP" >> "$GITHUB_ENV" XLINGS_BIN=$(cygpath -w "$USERPROFILE/.xlings/subos/default/bin/xlings.exe") echo "XLINGS_BIN=$XLINGS_BIN" >> "$GITHUB_ENV" echo "XLINGS_BIN_UNIX=$USERPROFILE/.xlings/subos/default/bin/xlings.exe" >> "$GITHUB_ENV" echo "XLINGS_XPKGS=$USERPROFILE/.xlings/data/xpkgs" >> "$GITHUB_ENV" - name: Build mcpp from source (self-host) shell: bash run: | export MCPP_VENDORED_XLINGS="$XLINGS_BIN" "$MCPP" build # Pick the NEWEST mcpp.exe, not an arbitrary one: `target/` is # restored from cache and keeps a directory per build fingerprint, # so after a version bump the freshly built binary sits alongside # the previous release's. `find | head -1` returned whichever the # directory walk hit first — which is how a 0.0.106 build ran the # 0.0.105 binary and failed 01_help_and_version. MCPP_BIN=$(find target -name "mcpp.exe" -path "*/bin/*" -printf "%T@ %p\n" \ | sort -rn | head -1 | cut -d" " -f2-) test -n "$MCPP_BIN" || { echo "FAIL: no mcpp.exe in target/"; exit 1; } MCPP_BIN=$(cd "$(dirname "$MCPP_BIN")" && pwd)/$(basename "$MCPP_BIN") echo "Self-hosted binary: $MCPP_BIN" "$MCPP_BIN" --version echo "MCPP_BIN=$MCPP_BIN" >> "$GITHUB_ENV" - name: Package Windows release zip id: stage shell: bash run: | VERSION="${{ steps.resolve.outputs.version }}" WRAPPER="mcpp-${VERSION}-windows-x86_64" ZIPNAME="${WRAPPER}.zip" STAGING=$(mktemp -d) mkdir -p "$STAGING/$WRAPPER/bin" "$STAGING/$WRAPPER/registry/bin" cp "$MCPP_BIN" "$STAGING/$WRAPPER/bin/mcpp.exe" # Windows batch launcher printf '@echo off\r\n"%%~dp0bin\\mcpp.exe" %%*\r\n' > "$STAGING/$WRAPPER/mcpp.bat" cp README.md "$STAGING/$WRAPPER/" 2>/dev/null || true cp LICENSE "$STAGING/$WRAPPER/" 2>/dev/null || true # Bundle xlings.exe for install consumers if [ -f "$XLINGS_BIN_UNIX" ]; then cp "$XLINGS_BIN_UNIX" "$STAGING/$WRAPPER/registry/bin/xlings.exe" fi # Pack with 7z (available on windows-latest) mkdir -p dist (cd "$STAGING" && 7z a -tzip "$ZIPNAME" "$WRAPPER") cp "$STAGING/$ZIPNAME" "dist/$ZIPNAME" # Versionless alias cp "dist/$ZIPNAME" "dist/mcpp-windows-x86_64.zip" # SHA256 (cd dist && sha256sum "$ZIPNAME" > "$ZIPNAME.sha256") (cd dist && sha256sum "mcpp-windows-x86_64.zip" > "mcpp-windows-x86_64.zip.sha256") echo "zipname=$ZIPNAME" >> "$GITHUB_OUTPUT" ls -la dist/ - name: Smoke-test the packaged zip shell: bash run: | ZIPNAME="${{ steps.stage.outputs.zipname }}" WRAPPER="${ZIPNAME%.zip}" SMOKE=$(mktemp -d) (cd "$SMOKE" && unzip -q "$GITHUB_WORKSPACE/dist/$ZIPNAME") "$SMOKE/$WRAPPER/bin/mcpp.exe" --version "$SMOKE/$WRAPPER/bin/mcpp.exe" --help | head -5 test -f "$SMOKE/$WRAPPER/registry/bin/xlings.exe" test -f "$SMOKE/$WRAPPER/mcpp.bat" echo "Smoke-test passed" - name: Upload Windows artifacts to release uses: softprops/action-gh-release@v2 with: tag_name: ${{ steps.resolve.outputs.tag }} files: | dist/mcpp-${{ steps.resolve.outputs.version }}-windows-x86_64.zip dist/mcpp-${{ steps.resolve.outputs.version }}-windows-x86_64.zip.sha256 dist/mcpp-windows-x86_64.zip dist/mcpp-windows-x86_64.zip.sha256 # Seal the complete, non-draft release inventory only after every platform # uploader has finished. The manifest is desired state for downstream # reconcilers: a rerun may reproduce it byte-for-byte, but may never replace # it with different bytes for the same tag. release-manifest: name: validate + seal release manifest needs: [build-release, build-linux-aarch64, build-macos, build-windows] runs-on: ubuntu-24.04 permissions: contents: write timeout-minutes: 20 env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Resolve immutable release identity id: resolve run: | if [ "${{ github.event_name }}" = "push" ]; then TAG="${{ github.ref_name }}" elif [ -n "${{ github.event.inputs.tag }}" ]; then TAG="${{ github.event.inputs.tag }}" else VERSION=$(awk -F '"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml) test -n "$VERSION" || { echo 'failed to read [package].version from mcpp.toml'; exit 1; } TAG="v$VERSION" fi git fetch --force origin "refs/tags/$TAG:refs/tags/$TAG" COMMIT=$(git rev-list -n 1 "refs/tags/$TAG") test -n "$COMMIT" echo "tag=$TAG" >> "$GITHUB_OUTPUT" echo "version=${TAG#v}" >> "$GITHUB_OUTPUT" echo "commit=$COMMIT" >> "$GITHUB_OUTPUT" - name: Generate, upload once, and refetch manifest run: | TAG="${{ steps.resolve.outputs.tag }}" VERSION="${{ steps.resolve.outputs.version }}" COMMIT="${{ steps.resolve.outputs.commit }}" AUDIT_ROOT=$(mktemp -d) mkdir -p "$AUDIT_ROOT/assets" "$AUDIT_ROOT/publish" gh api "repos/${GITHUB_REPOSITORY}/releases/tags/$TAG" \ > "$AUDIT_ROOT/release.json" gh release download "$TAG" --repo "$GITHUB_REPOSITORY" \ --dir "$AUDIT_ROOT/assets" # SHA256SUMS names every archive of the release, the four builds # and the source: the Linux job wrote it before the others had # uploaded theirs, so it named Linux x86_64 alone. (cd "$AUDIT_ROOT/assets" && ls mcpp-*.tar.gz mcpp-*.zip 2>/dev/null | LC_ALL=C sort \ | xargs sha256sum > "$AUDIT_ROOT/SHA256SUMS") cat "$AUDIT_ROOT/SHA256SUMS" if ! cmp -s "$AUDIT_ROOT/SHA256SUMS" "$AUDIT_ROOT/assets/SHA256SUMS"; then gh release upload "$TAG" "$AUDIT_ROOT/SHA256SUMS" --clobber --repo "$GITHUB_REPOSITORY" fi python3 tools/release/generate_manifest.py \ --release-json "$AUDIT_ROOT/release.json" \ --assets-dir "$AUDIT_ROOT/assets" \ --version "$VERSION" \ --tag "$TAG" \ --commit "$COMMIT" \ --output "$AUDIT_ROOT/publish/mcpp-release.json" if [ -f "$AUDIT_ROOT/assets/mcpp-release.json" ]; then cmp "$AUDIT_ROOT/assets/mcpp-release.json" \ "$AUDIT_ROOT/publish/mcpp-release.json" echo "Existing manifest is byte-identical; leaving it untouched." else gh release upload "$TAG" \ "$AUDIT_ROOT/publish/mcpp-release.json" \ --repo "$GITHUB_REPOSITORY" fi # Do not trust the upload command alone. Fetch the final public # inventory into a clean directory, recompute every payload digest, # regenerate desired state, and compare the published bytes. MANIFEST_VISIBLE=false for attempt in {1..12}; do if gh api "repos/${GITHUB_REPOSITORY}/releases/tags/$TAG" \ --jq '.assets[].name' | grep -Fxq mcpp-release.json; then MANIFEST_VISIBLE=true break fi echo "Waiting for mcpp-release.json API visibility ($attempt/12)" sleep 5 done test "$MANIFEST_VISIBLE" = true mkdir -p "$AUDIT_ROOT/final-assets" gh api "repos/${GITHUB_REPOSITORY}/releases/tags/$TAG" \ > "$AUDIT_ROOT/final-release.json" gh release download "$TAG" --repo "$GITHUB_REPOSITORY" \ --dir "$AUDIT_ROOT/final-assets" test -f "$AUDIT_ROOT/final-assets/mcpp-release.json" python3 tools/release/generate_manifest.py \ --release-json "$AUDIT_ROOT/final-release.json" \ --assets-dir "$AUDIT_ROOT/final-assets" \ --version "$VERSION" \ --tag "$TAG" \ --commit "$COMMIT" \ --output "$AUDIT_ROOT/final-expected.json" cmp "$AUDIT_ROOT/final-assets/mcpp-release.json" \ "$AUDIT_ROOT/final-expected.json" cat "$AUDIT_ROOT/final-assets/mcpp-release.json" echo "SEALED_RELEASE_MANIFEST=$AUDIT_ROOT/final-assets/mcpp-release.json" >> "$GITHUB_ENV" # Downstream notifications consume the identity sealed by THIS run, # including workflow_dispatch rebuilds of a tag other than main's version. - name: Save the sealed manifest for package notifications uses: actions/upload-artifact@v4 with: name: mcpp-release-manifest path: ${{ env.SEALED_RELEASE_MANIFEST }} if-no-files-found: error overwrite: true # A rerun has already proved the public bytes identical. retention-days: 30 # Publish this release into the xlings ecosystem, after ALL platform builds # have uploaded their assets and the immutable manifest gate has passed: # ① mirror binaries → xlings-res/mcpp (GitHub + GitCode) so XLINGS_RES # downloads resolve on every platform (incl. the CN/GitCode path); # ② open a PR against openxlings/xim-pkgindex bumping mcpp to this version # (a maintainer merges it — index git source is not on the critical path). # These publication steps are required for ecosystem completeness: their # failure leaves the GitHub Release object available but keeps this workflow # red, so post-release verification cannot report a complete release. # Shared vendored scripts live in .github/tools/ (kept in sync with xlings). publish-ecosystem: needs: release-manifest runs-on: ubuntu-latest # A4 hardening: a single stuck upload once held this job >1h (6h default # ceiling). The mirror script has per-file timeouts and (post-0.0.89) # batch-upload + ranged-GET verification — normal runs are minutes; 30 # is the generous backstop (20 was hit by the old per-asset verify loop). timeout-minutes: 30 env: XLINGS_RES_TOKEN: ${{ secrets.XLINGS_RES_TOKEN }} GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }} XIM_PKGINDEX_TOKEN: ${{ secrets.XIM_PKGINDEX_TOKEN }} steps: - name: Checkout code uses: actions/checkout@v4 - name: Determine version id: version run: echo "version=$(awk -F '\"' '/^version[[:space:]]*=/{print $2; exit}' mcpp.toml)" >> "$GITHUB_OUTPUT" - name: Mirror binaries to xlings-res/mcpp (gh + gtc) if: ${{ env.XLINGS_RES_TOKEN != '' }} # THIS MUST BE LONGER THAN THE SCRIPT'S OWN LEG DEADLINE, AND FOR # A LONG TIME IT WAS SHORTER. # # `mirror_res.sh` gives GitCode `MIRROR_LEG_DEADLINE_GTC=2400s` because # that host shapes inbound traffic; this step killed it at 600s, so the # script's budget could never be spent and the shorter of two # disagreeing limits always won. Every release since has ended the same # way — `The action … has timed out after 10 minutes`, GitHub's eight # assets uploaded in seconds and GitCode's three large ones cut off, # with only the small `.sha256` files through. v2026.8.25.1 and # v2026.8.25.2 both, measured. # # The paragraph this replaces described a design that no longer # exists: a per-asset `MIRROR_UPLOAD_TIMEOUT` of 180s. The script's own # comment records why it went ("the old per-asset cap failed four # releases in a row") — but this value, calibrated to it, stayed. # # 45 > 40 leaves the script room to reach its own deadline and report # what it abandoned by name, which is the visibility the value exists # for; the job's `timeout-minutes: 30` above is the outer backstop. timeout-minutes: 45 env: GH_TOKEN: ${{ secrets.XLINGS_RES_TOKEN }} run: | chmod +x .github/tools/gtc .github/tools/mirror_res.sh export PATH="$PWD/.github/tools:$PATH" # A4: BLOCKING. Both mirror hosts serve users (GLOBAL + CN install # paths); an incomplete mirror must fail here, visibly, instead of # surfacing as a 404 in the first user's install (or fresh-install CI). bash .github/tools/mirror_res.sh mcpp "${{ steps.version.outputs.version }}" - name: Open index bump PR (xim-pkgindex) if: ${{ env.XIM_PKGINDEX_TOKEN != '' }} env: PKGINDEX_TOKEN: ${{ secrets.XIM_PKGINDEX_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | chmod +x .github/tools/bump_index.sh # A4: BLOCKING — a missing bump PR means the index never learns the # release exists and every post-release install of the new version 404s. # The index's own updater writes the PR (bump_index.sh says why); # it needs the GitCode mirror above to be complete. bash .github/tools/bump_index.sh mcpp "${{ steps.version.outputs.version }}" # Post-release verification (ci-fresh-install) is triggered via its # `workflow_run: [release]` hook — a platform-generated event that is # exempt from GITHUB_TOKEN trigger suppression and needs no cross-repo # PAT. (A PAT-based dispatch step lived here briefly; it never worked — # XIM_PKGINDEX_TOKEN's resource owner is the index org and cannot cover # this repository.) |
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |