docs: retranslate the 简体中文 README and manual; pypi: pip install mcpp-bin #1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: pypi-publish | |
| # Publish the `mcpp-bin` wheels to PyPI (`pip install mcpp-bin`). | |
| # | |
| # Downstream of `release`, like aur-publish.yml and homebrew-publish.yml: the | |
| # wheels are built from the release's own mcpp-release.json and payloads, so | |
| # this runs only once the release workflow has completed. | |
| # | |
| # CREDENTIALS: none stored. Publishing uses PyPI Trusted Publishing (OIDC): | |
| # PyPI trusts this repository + workflow file + the `pypi` environment, and | |
| # the job exchanges its GitHub OIDC token for a short-lived upload token. | |
| # One-time setup is in scripts/pypi/README.md. | |
| # | |
| # ARMING: the automatic trigger builds, verifies and reports, and publishes | |
| # only when the repository variable PYPI_AUTOPUBLISH is `true`, for the reason | |
| # aur-publish.yml gives: an unattended push to a third-party service must be | |
| # armed by a human who has watched one publish succeed, not inherited from a | |
| # merge. `workflow_dispatch` carries its own explicit `publish` switch. | |
| on: | |
| workflow_run: | |
| workflows: [release] | |
| types: [completed] | |
| # Changes to the packaging itself build and pip-install the wheels of the | |
| # latest release on every platform. A pull request never publishes. | |
| pull_request: | |
| paths: | |
| - scripts/pypi/** | |
| - tests/scripts/test_pypi_wheels.py | |
| - .github/workflows/pypi-publish.yml | |
| workflow_dispatch: | |
| inputs: | |
| publish: | |
| description: 'Upload to PyPI (false builds and verifies only)' | |
| type: boolean | |
| required: true | |
| default: false | |
| tag: | |
| description: 'Release tag, e.g. v2026.9.21.3 (default: the latest release)' | |
| type: string | |
| required: false | |
| concurrency: | |
| group: pypi-mcpp-bin | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| jobs: | |
| build: | |
| name: build wheels | |
| if: >- | |
| github.event_name != 'workflow_run' || | |
| github.event.workflow_run.conclusion == 'success' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 20 | |
| outputs: | |
| version: ${{ steps.resolve.outputs.version }} | |
| publish: ${{ steps.resolve.outputs.publish }} | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.event.workflow_run.head_sha || github.ref }} | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - name: Builder contract tests | |
| run: python3 tests/scripts/test_pypi_wheels.py | |
| - name: Resolve the release and whether to publish | |
| id: resolve | |
| env: | |
| TRIGGER: ${{ github.event_name }} | |
| INPUT_TAG: ${{ inputs.tag }} | |
| MANUAL_PUBLISH: ${{ inputs.publish }} | |
| AUTOPUBLISH: ${{ vars.PYPI_AUTOPUBLISH }} | |
| run: | | |
| set -euo pipefail | |
| if [[ -n "${INPUT_TAG:-}" ]]; then | |
| tag="$INPUT_TAG" | |
| elif [[ "$TRIGGER" == "workflow_run" ]]; then | |
| # The released commit's mcpp.toml carries the released version. | |
| tag="v$(grep -m1 -E '^\s*version\s*=' mcpp.toml | sed -E 's/.*"([^"]+)".*/\1/')" | |
| else | |
| tag="$(gh release view -R "$GITHUB_REPOSITORY" --json tagName --jq .tagName)" | |
| fi | |
| version="${tag#v}" | |
| echo "tag=$tag" >> "$GITHUB_OUTPUT" | |
| echo "version=$version" >> "$GITHUB_OUTPUT" | |
| # PyPI never accepts the same file twice, so an existing version is | |
| # a finished job rather than something to retry. | |
| code=$(curl -s -o /dev/null -w '%{http_code}' --retry 3 --retry-all-errors \ | |
| "https://pypi.org/pypi/mcpp-bin/$version/json") | |
| if [[ "$code" == "200" ]]; then | |
| echo "::notice::mcpp-bin $version is already on PyPI; nothing to publish." | |
| publish=false | |
| elif [[ "$TRIGGER" == "workflow_run" ]]; then | |
| if [[ "${AUTOPUBLISH:-}" == "true" ]]; then | |
| publish=true | |
| else | |
| publish=false | |
| echo "::notice::PYPI_AUTOPUBLISH is not set — building and verifying $tag without publishing." | |
| fi | |
| elif [[ "$TRIGGER" == "workflow_dispatch" ]]; then | |
| publish="${MANUAL_PUBLISH:-false}" | |
| else | |
| publish=false | |
| fi | |
| echo "publish=$publish" >> "$GITHUB_OUTPUT" | |
| echo "mcpp-bin $version from $tag; publish=$publish" >> "$GITHUB_STEP_SUMMARY" | |
| - name: Build wheels from the release manifest | |
| run: python3 scripts/pypi/build_wheels.py --tag "${{ steps.resolve.outputs.tag }}" --out dist | |
| - name: Check metadata | |
| run: | | |
| python3 -m pip install --quiet twine | |
| python3 -m twine check --strict dist/*.whl | |
| - uses: actions/upload-artifact@v4 | |
| with: | |
| name: mcpp-bin-wheels | |
| path: dist/*.whl | |
| if-no-files-found: error | |
| # pip, not this workflow, picks the wheel: each runner installs from the | |
| # directory of all four, so a wrong platform tag fails here rather than on a | |
| # user's machine. The run then checks the two properties the launcher exists | |
| # for: the per-user home is outside the Python environment, and the bundled | |
| # xlings is the one seeded into it. | |
| smoke: | |
| name: pip install (${{ matrix.os }}) | |
| needs: build | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| os: [ubuntu-24.04, ubuntu-24.04-arm, macos-14, windows-latest] | |
| runs-on: ${{ matrix.os }} | |
| timeout-minutes: 20 | |
| steps: | |
| - uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.12' | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: mcpp-bin-wheels | |
| path: dist | |
| - name: Install and run | |
| shell: bash | |
| env: | |
| VERSION: ${{ needs.build.outputs.version }} | |
| run: | | |
| set -euo pipefail | |
| python -m venv venv | |
| if [[ -x venv/Scripts/python.exe ]]; then py=venv/Scripts/python.exe; bin=venv/Scripts; else py=venv/bin/python; bin=venv/bin; fi | |
| "$py" -m pip install --quiet --no-index --find-links dist mcpp-bin | |
| home="$RUNNER_TEMP/home"; mkdir -p "$home" | |
| export HOME="$home" USERPROFILE="$home" | |
| unset MCPP_HOME MCPP_VENDORED_XLINGS | |
| out="$("$bin/mcpp" --version)" | |
| echo "$out" | |
| [[ "$out" == *"$VERSION"* ]] || { echo "::error::expected $VERSION, got: $out"; exit 1; } | |
| "$bin/mcpp" self env | tee env.txt | |
| grep -F "MCPP_HOME" env.txt | grep -F ".mcpp" \ | |
| || { echo "::error::MCPP_HOME is not the per-user home"; exit 1; } | |
| if grep -F "MCPP_HOME" env.txt | grep -qF "site-packages"; then | |
| echo "::error::MCPP_HOME resolved into the Python environment"; exit 1 | |
| fi | |
| # On Windows mcpp runs the vendored xlings in place (src/config.cppm, | |
| # make_xlings_env), so the seeded copy is checked on POSIX only. | |
| if [[ "$RUNNER_OS" != "Windows" ]]; then | |
| ls "$home/.mcpp/registry/bin/" | grep -q '^xlings' \ | |
| || { echo "::error::the bundled xlings was not seeded into the home"; exit 1; } | |
| fi | |
| publish: | |
| name: publish to PyPI | |
| needs: [build, smoke] | |
| if: needs.build.outputs.publish == 'true' | |
| runs-on: ubuntu-24.04 | |
| timeout-minutes: 15 | |
| environment: | |
| name: pypi | |
| url: https://pypi.org/project/mcpp-bin/${{ needs.build.outputs.version }}/ | |
| permissions: | |
| id-token: write | |
| steps: | |
| - uses: actions/download-artifact@v4 | |
| with: | |
| name: mcpp-bin-wheels | |
| path: dist | |
| - uses: pypa/gh-action-pypi-publish@release/v1 | |
| with: | |
| packages-dir: dist/ |