feat: Add mirror and mirror-token inputs for custom Python distribution sources (#1302)

* feat: Add `mirror` and `mirror-token` inputs for custom Python distribution sources

Users who need custom CPython builds (internal mirrors, GHES-hosted forks,
special build configurations, compliance builds, air-gapped runners) could not
previously point setup-python at anything other than actions/python-versions.

Adds two new inputs:
- `mirror`: base URL hosting versions-manifest.json and the Python
  distributions it references. Defaults to the existing
  https://raw.githubusercontent.com/actions/python-versions/main.
- `mirror-token`: optional token used to authenticate requests to the mirror.

If `mirror` is a raw.githubusercontent.com/{owner}/{repo}/{branch} URL, the
manifest is fetched via the GitHub REST API (authenticated rate limit applies);
otherwise the action falls back to a direct GET of {mirror}/versions-manifest.json.

Token interaction
-----------------

`token` is never forwarded to arbitrary hosts. Auth resolution is per-URL:

  1. if mirror-token is set, use mirror-token
  2. else if token is set AND the target host is github.com,
     *.github.com, or *.githubusercontent.com, use token
  3. else send no auth

Cases:

  Default (no inputs set)
    mirror = default raw.githubusercontent.com URL, mirror-token empty,
    token = github.token.
    → manifest API call and tarball downloads use `token`.
    Identical to prior behavior.

  Custom raw.githubusercontent.com mirror (e.g. personal fork)
    mirror-token empty, token = github.token.
    → manifest API call and tarball downloads use `token`
      (target hosts are GitHub-owned).

  Custom non-GitHub mirror, no mirror-token
    mirror-token empty, token = github.token.
    → manifest fetched via direct URL (no auth attached),
      tarball downloads use no auth.
    `token` is NOT forwarded to the custom host — this is the
    leak-prevention case.

  Custom non-GitHub mirror with mirror-token
    mirror-token set, token may be set.
    → manifest fetch and tarball downloads use `mirror-token`.

  Custom GitHub mirror with both tokens set
    mirror-token wins. Used for both the manifest API call and
    tarball downloads.

* fix: address mirror review feedback

- scope mirror-token to the mirror host and send it verbatim
- route non-repo mirrors straight to the URL fetch instead of throwing
- authenticate the manifest fetch
- warn on slash branches, and on mirror with PyPy/GraalPy
- memoize mirror validation
- exercise the direct-URL path in the E2E job

Addresses https://github.com/actions/setup-python/pull/1302#issuecomment-5202618946

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix: correct mirror warnings, auth scoping, and integration coverage

- only warn about PyPy/GraalPy mirror when a custom mirror is set; the
  action.yml default made the warning fire on every run
- accept the refs/heads/{branch} raw URL form so it routes via the REST
  API instead of tripping the slash-branch warning
- scope mirror-token to the full mirror origin (scheme+host+port) so it
  can't leak to a same-host http download_url
- make an invalid mirror fatal on the auth path, matching getManifestUrl
- fix warning/docs that wrongly claimed the raw fallback is anonymous
- force a manifest fetch in the mirror integration job (check-latest) so
  it actually contacts the mirror instead of using the preinstalled cache

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Ludovic Henry
2026-09-09 00:31:48 +02:00
committed by GitHub
parent 751276eafc
commit 337b0725b8
8 changed files with 1038 additions and 53 deletions
+26
View File
@@ -61,6 +61,32 @@ jobs:
- name: Run simple code
run: python -c 'import math; print(math.factorial(5))'
setup-versions-via-mirror-input:
name: 'Setup via explicit mirror input: ${{ matrix.os }}'
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@v6
# The refs/heads/ form serves the same manifest as the default mirror and
# exercises the refs/heads/{branch} -> {branch} parsing on the API path.
# check-latest forces a manifest fetch even though 3.12 is preinstalled,
# so the job actually contacts the mirror instead of short-circuiting on
# the tool cache.
- name: setup-python with explicit mirror
uses: ./
with:
python-version: 3.12
check-latest: true
mirror: https://raw.githubusercontent.com/actions/python-versions/refs/heads/main
- name: Run simple code
run: python -c 'import sys; print(sys.version)'
setup-versions-from-file:
name: Setup ${{ matrix.python }} ${{ matrix.os }} version file
runs-on: ${{ matrix.os }}