Compare commits

..

1 Commits

Author SHA1 Message Date
eifinger-bot edc4037b1b fix: add timeout to fetch to prevent silent hangs
Add AbortSignal.timeout(30s) to fetch requests to ensure they fail
fast instead of hanging indefinitely when network issues occur.
This fixes issues where the action would hang and eventually get
killed by GitHub Actions without a clear error message.
2026-05-31 09:25:28 +02:00
47 changed files with 7346 additions and 10344 deletions
@@ -26,7 +26,6 @@ Use this skill when the user wants to:
- Inspect `package.json` before editing. - Inspect `package.json` before editing.
- Run `npm ci --ignore-scripts` before applying updates. - Run `npm ci --ignore-scripts` before applying updates.
- Use `npm install ... --ignore-scripts` for direct dependency changes so `package-lock.json` stays in sync. - Use `npm install ... --ignore-scripts` for direct dependency changes so `package-lock.json` stays in sync.
- When updating `@biomejs/biome`, also update the Biome schema URL version in `biome.json` to match the installed Biome version.
7. Run `npm run all`. 7. Run `npm run all`.
8. If requested, commit the changed source, lockfile, and generated artifacts, then push and open a PR. 8. If requested, commit the changed source, lockfile, and generated artifacts, then push and open a PR.
+4 -4
View File
@@ -41,13 +41,13 @@ jobs:
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL - name: Initialize CodeQL
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 uses: github/codeql-action/init@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
with: with:
languages: ${{ matrix.language }} languages: ${{ matrix.language }}
source-root: src source-root: src
@@ -59,7 +59,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below) # If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild - name: Autobuild
uses: github/codeql-action/autobuild@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 uses: github/codeql-action/autobuild@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
# ️ Command-line programs to run using the OS shell. # ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl # 📚 https://git.io/JvXDl
@@ -73,4 +73,4 @@ jobs:
# make release # make release
- name: Perform CodeQL Analysis - name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 uses: github/codeql-action/analyze@68bde559dea0fdcac2102bfdf6230c5f70eb485e # v4.35.4
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
pull-requests: read pull-requests: read
steps: steps:
- name: 🚀 Run Release Drafter - name: 🚀 Run Release Drafter
uses: release-drafter/release-drafter@4d75298e00d9e34c483e5ff8c68d0ea1c1940c1e # v7.5.1 uses: release-drafter/release-drafter@5de93583980a40bd78603b6dfdcda5b4df377b32 # v7.2.0
with: with:
commitish: ${{ github.sha }} commitish: ${{ github.sha }}
env: env:
-11
View File
@@ -111,14 +111,3 @@ jobs:
echo "Publishing draft release $TAG" echo "Publishing draft release $TAG"
gh release edit "$TAG" --draft=false gh release edit "$TAG" --draft=false
update-docs:
name: Update docs
needs:
- release
uses: ./.github/workflows/update-docs.yml
permissions:
contents: write
pull-requests: write
with:
tag: v${{ inputs.version }}
+47 -62
View File
@@ -21,14 +21,14 @@ jobs:
permissions: permissions:
security-events: write # for zizmor security-events: write # for zizmor
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Actionlint - name: Actionlint
uses: eifinger/actionlint-action@1fc89649be682d16ec5cf65ea16e269eb88d3982 # v1.10.2 uses: eifinger/actionlint-action@7802e0cc3ab3f81cbffb36fb0bf1a3621d994b89 # v1.10.1
- name: Run zizmor - name: Run zizmor
uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7 uses: zizmorcore/zizmor-action@a16621b09c6db4281f81a93cb393b05dcd7b7165 # v0.5.5
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with: with:
node-version-file: .nvmrc node-version-file: .nvmrc
cache: npm cache: npm
@@ -51,7 +51,7 @@ jobs:
matrix: matrix:
os: [ubuntu-latest, macos-latest, macos-14, windows-latest] os: [ubuntu-latest, macos-latest, macos-14, windows-latest]
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install latest version - name: Install latest version
@@ -76,7 +76,7 @@ jobs:
test-uv-no-modify-path: test-uv-no-modify-path:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install with UV_NO_MODIFY_PATH set - name: Install with UV_NO_MODIFY_PATH set
@@ -125,7 +125,7 @@ jobs:
expected-version: "0.1.0" expected-version: "0.1.0"
resolution-strategy: "lowest" resolution-strategy: "lowest"
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install version ${{ matrix.input.version-input }} with strategy ${{ matrix.input.resolution-strategy || 'highest' }} - name: Install version ${{ matrix.input.version-input }} with strategy ${{ matrix.input.resolution-strategy || 'highest' }}
@@ -154,7 +154,7 @@ jobs:
matrix: matrix:
version-input: ["latest", ">=0.8"] version-input: ["latest", ">=0.8"]
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install version ${{ matrix.version-input }} - name: Install version ${{ matrix.version-input }}
@@ -194,7 +194,7 @@ jobs:
- working-directory: "__tests__/fixtures/uv-toml-project" - working-directory: "__tests__/fixtures/uv-toml-project"
expected-version: "0.5.15" expected-version: "0.5.15"
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install version from ${{ matrix.input.working-directory }} - name: Install version from ${{ matrix.input.working-directory }}
@@ -220,7 +220,7 @@ jobs:
- version-file: "__tests__/fixtures/.tool-versions" - version-file: "__tests__/fixtures/.tool-versions"
expected-version: "0.5.15" expected-version: "0.5.15"
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install version from ${{ matrix.input.version-file }} - name: Install version from ${{ matrix.input.version-file }}
@@ -237,7 +237,7 @@ jobs:
test-malformed-pyproject-file-fallback: test-malformed-pyproject-file-fallback:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install using malformed pyproject.toml - name: Install using malformed pyproject.toml
@@ -257,7 +257,7 @@ jobs:
- os: macos-latest - os: macos-latest
checksum: "a70cbfbf3bb5c08b2f84963b4f12c94e08fbb2468ba418a3bfe1066fbe9e7218" checksum: "a70cbfbf3bb5c08b2f84963b4f12c94e08fbb2468ba418a3bfe1066fbe9e7218"
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Checksum matches expected - name: Checksum matches expected
@@ -271,7 +271,7 @@ jobs:
test-with-explicit-token: test-with-explicit-token:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install default version - name: Install default version
@@ -284,7 +284,7 @@ jobs:
test-uvx: test-uvx:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install default version - name: Install default version
@@ -297,7 +297,7 @@ jobs:
matrix: matrix:
os: [ubuntu-latest, macos-latest, macos-14, windows-latest] os: [ubuntu-latest, macos-latest, macos-14, windows-latest]
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install default version - name: Install default version
@@ -311,7 +311,7 @@ jobs:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install latest version - name: Install latest version
@@ -343,7 +343,7 @@ jobs:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install latest version - name: Install latest version
@@ -382,7 +382,7 @@ jobs:
matrix: matrix:
os: [ubuntu-latest, macos-latest, windows-latest] os: [ubuntu-latest, macos-latest, windows-latest]
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install latest version - name: Install latest version
@@ -433,7 +433,7 @@ jobs:
test-activate-environment-no-project: test-activate-environment-no-project:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Create incompatible pyproject.toml - name: Create incompatible pyproject.toml
@@ -477,7 +477,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: debian:unstable container: debian:unstable
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install latest version - name: Install latest version
@@ -491,7 +491,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
container: alpine container: alpine
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install latest version - name: Install latest version
@@ -530,7 +530,7 @@ jobs:
- os: windows-2025 - os: windows-2025
expected-os: "windows-2025" expected-os: "windows-2025"
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup uv - name: Setup uv
@@ -556,7 +556,7 @@ jobs:
enable-cache: ["true", "false", "auto"] enable-cache: ["true", "false", "auto"]
os: ["ubuntu-latest", "windows-latest"] os: ["ubuntu-latest", "windows-latest"]
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup with cache - name: Setup with cache
@@ -575,7 +575,7 @@ jobs:
os: ["ubuntu-latest", "windows-latest"] os: ["ubuntu-latest", "windows-latest"]
needs: test-setup-cache needs: test-setup-cache
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Restore with cache - name: Restore with cache
@@ -609,7 +609,7 @@ jobs:
test-setup-cache-requirements-txt: test-setup-cache-requirements-txt:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup with cache - name: Setup with cache
@@ -625,7 +625,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: test-setup-cache-requirements-txt needs: test-setup-cache-requirements-txt
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Restore with cache - name: Restore with cache
@@ -649,7 +649,7 @@ jobs:
test-setup-cache-dependency-glob: test-setup-cache-dependency-glob:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup with cache - name: Setup with cache
@@ -666,7 +666,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: test-setup-cache-dependency-glob needs: test-setup-cache-dependency-glob
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Change pyproject.toml - name: Change pyproject.toml
@@ -694,7 +694,7 @@ jobs:
test-setup-cache-save-cache-false: test-setup-cache-save-cache-false:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup with cache - name: Setup with cache
@@ -710,7 +710,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: test-setup-cache-save-cache-false needs: test-setup-cache-save-cache-false
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Restore with cache - name: Restore with cache
@@ -730,7 +730,7 @@ jobs:
test-setup-cache-restore-cache-false: test-setup-cache-restore-cache-false:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup with cache - name: Setup with cache
@@ -745,7 +745,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: test-setup-cache-restore-cache-false needs: test-setup-cache-restore-cache-false
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Restore with cache - name: Restore with cache
@@ -773,7 +773,7 @@ jobs:
expected-cache-dir: "D:\\a\\_temp\\setup-uv-cache" expected-cache-dir: "D:\\a\\_temp\\setup-uv-cache"
runs-on: ${{ matrix.inputs.os }} runs-on: ${{ matrix.inputs.os }}
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup with cache - name: Setup with cache
@@ -791,7 +791,7 @@ jobs:
test-cache-local-cache-disabled: test-cache-local-cache-disabled:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup without cache - name: Setup without cache
@@ -810,7 +810,7 @@ jobs:
test-cache-local-cache-disabled-but-explicit-path: test-cache-local-cache-disabled-but-explicit-path:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup without cache - name: Setup without cache
@@ -830,7 +830,7 @@ jobs:
test-no-python-version: test-no-python-version:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Fake pyproject.toml at root - name: Fake pyproject.toml at root
@@ -845,7 +845,7 @@ jobs:
test-custom-manifest-file: test-custom-manifest-file:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install from custom manifest file - name: Install from custom manifest file
@@ -861,24 +861,10 @@ jobs:
exit 1 exit 1
fi fi
test-download-from-astral-mirror-false:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
with:
persist-credentials: false
- name: Install with download-from-astral-mirror disabled
id: setup-uv
uses: ./
with:
download-from-astral-mirror: false
- name: Verify uv is installed
run: uv --version
test-absolute-path: test-absolute-path:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Create requirements.txt - name: Create requirements.txt
@@ -898,7 +884,7 @@ jobs:
test-relative-path: test-relative-path:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: mkdir - name: mkdir
@@ -922,7 +908,7 @@ jobs:
test-cache-prune-force: test-cache-prune-force:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup uv - name: Setup uv
@@ -939,7 +925,7 @@ jobs:
test-cache-dir-from-file: test-cache-dir-from-file:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Verify uv cache dir is not populated - name: Verify uv cache dir is not populated
@@ -966,7 +952,7 @@ jobs:
env: env:
UV_PYTHON_INSTALL_DIR: /tmp/missing-uv-python UV_PYTHON_INSTALL_DIR: /tmp/missing-uv-python
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Setup uv with cache and python cache enabled - name: Setup uv with cache and python cache enabled
@@ -987,7 +973,7 @@ jobs:
test-cache-python-installs: test-cache-python-installs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Verify Python install dir is not populated - name: Verify Python install dir is not populated
@@ -1014,7 +1000,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: test-cache-python-installs needs: test-cache-python-installs
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Verify Python install dir does not exist - name: Verify Python install dir does not exist
@@ -1056,7 +1042,7 @@ jobs:
expected-python-dir: "D:\\a\\_temp\\uv-python-dir" expected-python-dir: "D:\\a\\_temp\\uv-python-dir"
runs-on: ${{ matrix.inputs.os }} runs-on: ${{ matrix.inputs.os }}
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install latest version - name: Install latest version
@@ -1075,7 +1061,7 @@ jobs:
test-act: test-act:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Install act - name: Install act
@@ -1090,7 +1076,7 @@ jobs:
validate-typings: validate-typings:
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: false persist-credentials: false
- name: Validate typings - name: Validate typings
@@ -1133,7 +1119,6 @@ jobs:
- test-restore-cache-restore-cache-false - test-restore-cache-restore-cache-false
- test-no-python-version - test-no-python-version
- test-custom-manifest-file - test-custom-manifest-file
- test-download-from-astral-mirror-false
- test-absolute-path - test-absolute-path
- test-relative-path - test-relative-path
- test-cache-prune-force - test-cache-prune-force
+6 -24
View File
@@ -1,46 +1,28 @@
name: "Update docs" name: "Update docs"
on: on:
workflow_call: push:
inputs: tags:
tag: - "v*.*.*"
description: "Release tag to update docs for (e.g., v8.2.0)"
required: true
type: string
workflow_dispatch:
permissions: {} permissions: {}
jobs: jobs:
update-docs: update-docs:
continue-on-error: ${{ github.event_name == 'workflow_call' }}
runs-on: ubuntu-24.04-arm runs-on: ubuntu-24.04-arm
permissions: permissions:
contents: write contents: write
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
fetch-depth: 0
persist-credentials: true persist-credentials: true
- name: Get tag info - name: Get tag info
id: tag-info id: tag-info
run: | run: |
if [ -n "$INPUT_TAG" ]; then TAG_NAME="${GITHUB_REF#refs/tags/}"
TAG_NAME="$INPUT_TAG"
else
TAG_NAME=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1)
fi
if [[ ! "$TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
echo "::error::Tag must match vMAJOR.MINOR.PATCH (e.g., v8.2.0)"
exit 1
fi
COMMIT_SHA=$(git rev-list -n 1 "$TAG_NAME") COMMIT_SHA=$(git rev-list -n 1 "$TAG_NAME")
echo "tag=$TAG_NAME" >> "$GITHUB_OUTPUT" echo "tag=$TAG_NAME" >> "$GITHUB_OUTPUT"
echo "sha=$COMMIT_SHA" >> "$GITHUB_OUTPUT" echo "sha=$COMMIT_SHA" >> "$GITHUB_OUTPUT"
env:
INPUT_TAG: ${{ inputs.tag }}
- name: Update references in docs - name: Update references in docs
run: | run: |
OLD_REF=$(grep -oh 'astral-sh/setup-uv@[a-f0-9]\{40\} # v[0-9][^ ]*' README.md docs/*.md | head -1) OLD_REF=$(grep -oh 'astral-sh/setup-uv@[a-f0-9]\{40\} # v[0-9][^ ]*' README.md docs/*.md | head -1)
@@ -82,6 +64,6 @@ jobs:
Update `uses: astral-sh/setup-uv@...` references in documentation to Update `uses: astral-sh/setup-uv@...` references in documentation to
`${{ steps.tag-info.outputs.sha }} # ${{ steps.tag-info.outputs.tag }}`. `${{ steps.tag-info.outputs.sha }} # ${{ steps.tag-info.outputs.tag }}`.
base: main base: main
labels: "automated-pr,documentation" labels: "automated-pr,update-docs"
branch: update-docs-${{ steps.tag-info.outputs.tag }} branch: update-docs-${{ steps.tag-info.outputs.tag }}
delete-branch: true delete-branch: true
+2 -2
View File
@@ -15,10 +15,10 @@ jobs:
contents: write contents: write
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with: with:
persist-credentials: true persist-credentials: true
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with: with:
node-version-file: .nvmrc node-version-file: .nvmrc
cache: npm cache: npm
+10 -16
View File
@@ -26,7 +26,7 @@ Set up your GitHub Actions workflow with a specific version of [uv](https://docs
```yaml ```yaml
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
``` ```
If you do not specify a version, this action will look for a [required-version](https://docs.astral.sh/uv/reference/settings/#required-version) If you do not specify a version, this action will look for a [required-version](https://docs.astral.sh/uv/reference/settings/#required-version)
@@ -42,12 +42,12 @@ Have a look under [Advanced Configuration](#advanced-configuration) for detailed
```yaml ```yaml
- name: Install uv with all available options - name: Install uv with all available options
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
# The version of uv to install (default: searches for version in config files, then latest) # The version of uv to install (default: searches for version in config files, then latest)
version: "" version: ""
# Path to a file containing the version of uv to install, e.g., uv.toml, pyproject.toml, .tool-versions, requirements.txt or uv.lock (default: searches uv.toml then pyproject.toml) # Path to a file containing the version of uv to install (default: searches uv.toml then pyproject.toml)
version-file: "" version-file: ""
# Resolution strategy when resolving version ranges: 'highest' or 'lowest' # Resolution strategy when resolving version ranges: 'highest' or 'lowest'
@@ -100,7 +100,7 @@ Have a look under [Advanced Configuration](#advanced-configuration) for detailed
cache-local-path: "" cache-local-path: ""
# Prune cache before saving # Prune cache before saving
prune-cache: "false" prune-cache: "true"
# Upload managed Python installations to the GitHub Actions cache # Upload managed Python installations to the GitHub Actions cache
cache-python: "false" cache-python: "false"
@@ -120,14 +120,8 @@ Have a look under [Advanced Configuration](#advanced-configuration) for detailed
# URL to a custom manifest file in the astral-sh/versions format # URL to a custom manifest file in the astral-sh/versions format
manifest-file: "" manifest-file: ""
# Download uv from the Astral mirror instead of directly from GitHub Releases
download-from-astral-mirror: "true"
# Add problem matchers # Add problem matchers
add-problem-matchers: "true" add-problem-matchers: "true"
# Suppress info-level log output. Only warnings and errors are shown
quiet: "false"
``` ```
### Outputs ### Outputs
@@ -148,7 +142,7 @@ This will override any python version specifications in `pyproject.toml` and `.p
```yaml ```yaml
- name: Install the latest version of uv and set the python version to 3.13t - name: Install the latest version of uv and set the python version to 3.13t
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
python-version: 3.13t python-version: 3.13t
- run: uv pip install --python=3.13t pip - run: uv pip install --python=3.13t pip
@@ -166,7 +160,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v5
- name: Install the latest version of uv and set the python version - name: Install the latest version of uv and set the python version
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
python-version: ${{ matrix.python-version }} python-version: ${{ matrix.python-version }}
- name: Test with python ${{ matrix.python-version }} - name: Test with python ${{ matrix.python-version }}
@@ -183,7 +177,7 @@ It also controls where [the venv gets created](#activate-environment), unless `v
```yaml ```yaml
- name: Install uv based on the config files in the working-directory - name: Install uv based on the config files in the working-directory
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
working-directory: my/subproject/dir working-directory: my/subproject/dir
``` ```
@@ -225,7 +219,7 @@ For example:
- name: Checkout the repository - name: Checkout the repository
uses: actions/checkout@main uses: actions/checkout@main
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
- name: Test - name: Test
@@ -237,7 +231,7 @@ To install a specific version of Python, use
```yaml ```yaml
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
- name: Install Python 3.12 - name: Install Python 3.12
@@ -256,7 +250,7 @@ output:
uses: actions/checkout@main uses: actions/checkout@main
- name: Install the default version of uv - name: Install the default version of uv
id: setup-uv id: setup-uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
- name: Print the installed version - name: Print the installed version
run: echo "Installed uv version is ${{ steps.setup-uv.outputs.uv-version }}" run: echo "Installed uv version is ${{ steps.setup-uv.outputs.uv-version }}"
``` ```
-5
View File
@@ -1,5 +0,0 @@
# Security policy
Report suspected vulnerabilities according to [Astral's security policy](https://github.com/astral-sh/.github/blob/main/SECURITY.md).
For this repository's security boundaries and reporting criteria, see the [setup-uv threat model](docs/threat-model.md).
-74
View File
@@ -1,74 +0,0 @@
import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import { createSetupInputs } from "../helpers/setup-inputs";
const mockRestoreCache = jest.fn();
const mockSaveState = jest.fn();
const mockSetOutput = jest.fn();
jest.unstable_mockModule("@actions/cache", () => ({
restoreCache: mockRestoreCache,
}));
jest.unstable_mockModule("@actions/core", () => ({
saveState: mockSaveState,
setOutput: mockSetOutput,
}));
jest.unstable_mockModule("../../src/hash/hash-files", () => ({
hashFiles: jest.fn(async () => "dependencyhash"),
}));
jest.unstable_mockModule("../../src/utils/logging", () => ({
info: jest.fn(),
warning: jest.fn(),
}));
jest.unstable_mockModule("../../src/utils/platforms", () => ({
getArch: jest.fn(() => "x86_64"),
getOSNameVersion: jest.fn(() => "ubuntu-24.04"),
getPlatform: jest.fn(async () => "unknown-linux-gnu"),
}));
const { restoreCache } = await import("../../src/cache/restore-cache");
function cacheKeyOutput(): string {
const call = mockSetOutput.mock.calls.find(([name]) => name === "cache-key");
expect(call).toBeDefined();
return call?.[1] as string;
}
beforeEach(() => {
jest.clearAllMocks();
});
describe("restoreCache", () => {
it("encodes Python version ranges before adding them to the cache key", async () => {
await restoreCache(createSetupInputs(), ">3.10.11,<3.11");
const cacheKey = cacheKeyOutput();
expect(cacheKey).not.toContain(",");
expect(cacheKey).toContain("-%3E3.10.11%2C%3C3.11-");
});
it("encodes cache suffixes before adding them to the cache key", async () => {
const inputs = createSetupInputs({ cacheSuffix: "tests-3.10,3.11" });
await restoreCache(inputs, "3.11");
const cacheKey = cacheKeyOutput();
expect(cacheKey).not.toContain(",");
expect(cacheKey).toContain("-tests-3.10%2C3.11");
});
it("uses an unpruned cache key by default", async () => {
const inputs = createSetupInputs({ cacheSuffix: "tests-3.11" });
await restoreCache(inputs, "3.11");
expect(cacheKeyOutput()).toBe(
"setup-uv-2-x86_64-unknown-linux-gnu-ubuntu-24.04-3.11-dependencyhash-tests-3.11",
);
});
});
+4 -64
View File
@@ -1,6 +1,5 @@
import { beforeEach, describe, expect, it, jest } from "@jest/globals"; import { beforeEach, describe, expect, it, jest } from "@jest/globals";
import * as semver from "semver"; import * as semver from "semver";
import { VERSIONS_MANIFEST_URL } from "../../src/utils/constants";
const mockInfo = jest.fn(); const mockInfo = jest.fn();
const mockWarning = jest.fn(); const mockWarning = jest.fn();
@@ -37,14 +36,11 @@ const mockGetLatestVersion = jest.fn<any>();
// biome-ignore lint/suspicious/noExplicitAny: Mock requires flexible typing in tests. // biome-ignore lint/suspicious/noExplicitAny: Mock requires flexible typing in tests.
const mockGetAllVersions = jest.fn<any>(); const mockGetAllVersions = jest.fn<any>();
// biome-ignore lint/suspicious/noExplicitAny: Mock requires flexible typing in tests. // biome-ignore lint/suspicious/noExplicitAny: Mock requires flexible typing in tests.
const mockGetFirstMatchingVersion = jest.fn<any>();
// biome-ignore lint/suspicious/noExplicitAny: Mock requires flexible typing in tests.
const mockGetArtifact = jest.fn<any>(); const mockGetArtifact = jest.fn<any>();
jest.unstable_mockModule("../../src/download/manifest", () => ({ jest.unstable_mockModule("../../src/download/manifest", () => ({
getAllVersions: mockGetAllVersions, getAllVersions: mockGetAllVersions,
getArtifact: mockGetArtifact, getArtifact: mockGetArtifact,
getFirstMatchingVersion: mockGetFirstMatchingVersion,
getLatestVersion: mockGetLatestVersion, getLatestVersion: mockGetLatestVersion,
})); }));
@@ -69,7 +65,6 @@ describe("download-version", () => {
mockCacheDir.mockReset(); mockCacheDir.mockReset();
mockGetLatestVersion.mockReset(); mockGetLatestVersion.mockReset();
mockGetAllVersions.mockReset(); mockGetAllVersions.mockReset();
mockGetFirstMatchingVersion.mockReset();
mockGetArtifact.mockReset(); mockGetArtifact.mockReset();
mockValidateChecksum.mockReset(); mockValidateChecksum.mockReset();
@@ -90,43 +85,14 @@ describe("download-version", () => {
expect(mockGetLatestVersion).toHaveBeenCalledWith(undefined); expect(mockGetLatestVersion).toHaveBeenCalledWith(undefined);
}); });
it("stops at the first matching version in the default manifest", async () => { it("uses the default manifest to resolve available versions", async () => {
mockGetFirstMatchingVersion.mockImplementation( mockGetAllVersions.mockResolvedValue(["0.9.26", "0.9.25"]);
(predicate: (version: string) => boolean) =>
["0.9.26", "0.9.25"].find(predicate),
);
const version = await resolveVersion("^0.9.0", undefined); const version = await resolveVersion("^0.9.0", undefined);
expect(version).toBe("0.9.26"); expect(version).toBe("0.9.26");
expect(mockGetFirstMatchingVersion).toHaveBeenCalledTimes(1); expect(mockGetAllVersions).toHaveBeenCalledTimes(1);
expect(mockGetAllVersions).not.toHaveBeenCalled(); expect(mockGetAllVersions).toHaveBeenCalledWith(undefined);
});
it("streams ranges when the default manifest URL is explicit", async () => {
mockGetFirstMatchingVersion.mockImplementation(
(predicate: (version: string) => boolean) =>
["0.9.26", "0.9.25"].find(predicate),
);
const version = await resolveVersion("^0.9.0", VERSIONS_MANIFEST_URL);
expect(version).toBe("0.9.26");
expect(mockGetFirstMatchingVersion).toHaveBeenCalledTimes(1);
expect(mockGetAllVersions).not.toHaveBeenCalled();
});
it("streams PEP 440 ranges from the default manifest", async () => {
mockGetFirstMatchingVersion.mockImplementation(
(predicate: (version: string) => boolean) =>
["0.9.26", "0.9.25"].find(predicate),
);
const version = await resolveVersion("!=0.9.26", undefined);
expect(version).toBe("0.9.25");
expect(mockGetFirstMatchingVersion).toHaveBeenCalledTimes(1);
expect(mockGetAllVersions).not.toHaveBeenCalled();
}); });
it("treats == exact pins as explicit versions", async () => { it("treats == exact pins as explicit versions", async () => {
@@ -410,32 +376,6 @@ describe("download-version", () => {
"0.9.26", "0.9.26",
); );
}); });
it("skips the Astral mirror when downloadFromAstralMirror is false", async () => {
mockGetArtifact.mockResolvedValue({
archiveFormat: "tar.gz",
checksum: "abc123",
downloadUrl:
"https://github.com/astral-sh/uv/releases/download/0.9.26/uv-x86_64-unknown-linux-gnu.tar.gz",
});
await downloadVersion(
"unknown-linux-gnu",
"x86_64",
"0.9.26",
undefined,
"token",
undefined,
false,
);
expect(mockDownloadTool).toHaveBeenCalledWith(
"https://github.com/astral-sh/uv/releases/download/0.9.26/uv-x86_64-unknown-linux-gnu.tar.gz",
undefined,
"token",
);
expect(mockDownloadTool).toHaveBeenCalledTimes(1);
});
}); });
describe("rewriteToMirror", () => { describe("rewriteToMirror", () => {
-150
View File
@@ -17,7 +17,6 @@ const {
fetchManifest, fetchManifest,
getAllVersions, getAllVersions,
getArtifact, getArtifact,
getFirstMatchingVersion,
getLatestVersion, getLatestVersion,
parseManifest, parseManifest,
} = await import("../../src/download/manifest"); } = await import("../../src/download/manifest");
@@ -34,7 +33,6 @@ function createMockResponse(
data: string, data: string,
) { ) {
return { return {
body: null,
ok, ok,
status, status,
statusText, statusText,
@@ -42,31 +40,6 @@ function createMockResponse(
}; };
} }
function createStreamingMockResponse(
chunks: string[],
cancel: () => void | Promise<void> = () => {},
close = false,
) {
const encoder = new TextEncoder();
return {
body: new ReadableStream<Uint8Array>({
cancel,
start(controller) {
for (const chunk of chunks) {
controller.enqueue(encoder.encode(chunk));
}
if (close) {
controller.close();
}
},
}),
ok: true,
status: 200,
statusText: "OK",
text: async () => chunks.join(""),
};
}
describe("manifest", () => { describe("manifest", () => {
beforeEach(() => { beforeEach(() => {
clearManifestCache(); clearManifestCache();
@@ -132,75 +105,6 @@ describe("manifest", () => {
getLatestVersion("https://example.com/custom.ndjson"), getLatestVersion("https://example.com/custom.ndjson"),
).resolves.toBe("0.9.26"); ).resolves.toBe("0.9.26");
}); });
it("stops reading the default manifest after the first record", async () => {
const [latestVersion] = sampleManifestResponse.split("\n");
const cancel = jest.fn();
mockFetch.mockResolvedValue(
createStreamingMockResponse(
[
latestVersion.slice(0, 100),
`${latestVersion.slice(100)}\n`,
"invalid trailing data\n",
],
cancel,
),
);
await expect(getLatestVersion()).resolves.toBe("0.9.26");
await expect(
getArtifact("0.9.26", "aarch64", "apple-darwin"),
).resolves.toBeDefined();
expect(cancel).toHaveBeenCalledTimes(1);
expect(mockFetch).toHaveBeenCalledTimes(1);
});
it("does not fail when canceling the remaining response fails", async () => {
const [latestVersion] = sampleManifestResponse.split("\n");
mockFetch.mockResolvedValue(
createStreamingMockResponse([`${latestVersion}\n`], () =>
Promise.reject(new Error("cancel failed")),
),
);
await expect(getLatestVersion()).resolves.toBe("0.9.26");
});
});
describe("getFirstMatchingVersion", () => {
it("stops at the first matching record", async () => {
const cancel = jest.fn();
mockFetch.mockResolvedValue(
createStreamingMockResponse(
[`${sampleManifestResponse}\n`, "invalid trailing data\n"],
cancel,
),
);
await expect(
getFirstMatchingVersion((version) => version === "0.9.25"),
).resolves.toBe("0.9.25");
expect(cancel).toHaveBeenCalledTimes(1);
});
it("caches a fully consumed response stream", async () => {
mockFetch.mockResolvedValue(
createStreamingMockResponse(
[`${sampleManifestResponse}\n`],
undefined,
true,
),
);
await expect(
getFirstMatchingVersion((version) => version === "0.0.1"),
).resolves.toBeUndefined();
await expect(getLatestVersion()).resolves.toBe("0.9.26");
expect(mockFetch).toHaveBeenCalledTimes(1);
});
}); });
describe("getArtifact", () => { describe("getArtifact", () => {
@@ -264,60 +168,6 @@ describe("manifest", () => {
expect(artifact).toBeUndefined(); expect(artifact).toBeUndefined();
}); });
it("does not cache records from a failed stream read", async () => {
const [latestVersion] = sampleManifestResponse.split("\n");
mockFetch
.mockResolvedValueOnce(
createStreamingMockResponse([
`${latestVersion}\n`,
"invalid manifest record\n",
]),
)
.mockResolvedValueOnce(
createMockResponse(true, 200, "OK", sampleManifestResponse),
);
await expect(
getArtifact("0.0.1", "aarch64", "apple-darwin"),
).rejects.toThrow("Failed to parse manifest data");
await expect(getLatestVersion()).resolves.toBe("0.9.26");
expect(mockFetch).toHaveBeenCalledTimes(2);
});
it("does not cache records when the response stream fails", async () => {
const [latestVersion] = sampleManifestResponse.split("\n");
const encoder = new TextEncoder();
let sentVersion = false;
const body = new ReadableStream<Uint8Array>({
pull(controller) {
if (!sentVersion) {
sentVersion = true;
controller.enqueue(encoder.encode(`${latestVersion}\n`));
return;
}
controller.error(new Error("response stream failed"));
},
});
mockFetch
.mockResolvedValueOnce({
body,
ok: true,
status: 200,
statusText: "OK",
})
.mockResolvedValueOnce(
createMockResponse(true, 200, "OK", sampleManifestResponse),
);
await expect(
getArtifact("0.0.1", "aarch64", "apple-darwin"),
).rejects.toThrow("response stream failed");
await expect(getLatestVersion()).resolves.toBe("0.9.26");
expect(mockFetch).toHaveBeenCalledTimes(2);
});
}); });
describe("parseManifest", () => { describe("parseManifest", () => {
-36
View File
@@ -1,36 +0,0 @@
import { CacheLocalSource, type SetupInputs } from "../../src/utils/inputs";
export function createSetupInputs(
overrides: Partial<SetupInputs> = {},
): SetupInputs {
return {
activateEnvironment: false,
addProblemMatchers: false,
cacheDependencyGlob: "uv.lock",
cacheLocalPath: {
path: "/tmp/setup-uv-cache",
source: CacheLocalSource.Input,
},
cachePython: false,
cacheSuffix: "",
checksum: "",
downloadFromAstralMirror: false,
enableCache: true,
githubToken: "",
ignoreEmptyWorkdir: false,
ignoreNothingToCache: false,
noProject: false,
pruneCache: false,
pythonDir: "/tmp/uv-python-dir",
pythonVersion: "",
quiet: false,
resolutionStrategy: "highest",
restoreCache: false,
saveCache: true,
venvPath: "/workspace/.venv",
version: "",
versionFile: "",
workingDirectory: "/workspace",
...overrides,
};
}
@@ -1,9 +1,5 @@
import { expect, test } from "@jest/globals"; import { expect, test } from "@jest/globals";
import { getUvVersionFromFile } from "../../src/version/file-parser"; import { getUvVersionFromFile } from "../../src/version/file-parser";
import {
getUvVersionFromPyprojectContent,
getUvVersionFromRequirementsText,
} from "../../src/version/requirements-file";
test("ignores dependencies starting with uv", async () => { test("ignores dependencies starting with uv", async () => {
const parsedVersion = getUvVersionFromFile( const parsedVersion = getUvVersionFromFile(
@@ -11,24 +7,3 @@ test("ignores dependencies starting with uv", async () => {
); );
expect(parsedVersion).toBe("0.6.17"); expect(parsedVersion).toBe("0.6.17");
}); });
test.each([
["without space before marker", "uv==0.11.20; sys_platform != 'emscripten'"],
["with space before marker", "uv==0.11.20 ; sys_platform != 'emscripten'"],
])("strips PEP 508 markers from pyproject dependency groups %s", (_, dependency) => {
const parsedVersion = getUvVersionFromPyprojectContent(`[dependency-groups]
test = [
"${dependency}",
]
`);
expect(parsedVersion).toBe("==0.11.20");
});
test("strips PEP 508 markers from requirements dependencies", () => {
const parsedVersion = getUvVersionFromRequirementsText(
"uv==0.11.20; sys_platform != 'emscripten'",
);
expect(parsedVersion).toBe("==0.11.20");
});
-36
View File
@@ -1,36 +0,0 @@
import { describe, expect, it } from "@jest/globals";
import { getUvVersionFromUvLockContent } from "../../src/version/uv-lock-file";
const UV_LOCK = `version = 1
requires-python = ">=3.12"
[[package]]
name = "anyio"
version = "4.6.0"
source = { registry = "https://pypi.org/simple" }
[[package]]
name = "uv"
version = "0.8.17"
source = { registry = "https://pypi.org/simple" }
`;
describe("getUvVersionFromUvLockContent", () => {
it("returns the exact uv version locked in uv.lock", () => {
expect(getUvVersionFromUvLockContent(UV_LOCK)).toBe("0.8.17");
});
it("returns undefined when uv is not a locked package", () => {
const content = `version = 1
[[package]]
name = "anyio"
version = "4.6.0"
`;
expect(getUvVersionFromUvLockContent(content)).toBeUndefined();
});
it("returns undefined when there are no packages", () => {
expect(getUvVersionFromUvLockContent("version = 1\n")).toBeUndefined();
});
});
@@ -63,24 +63,6 @@ describe("resolveVersionRequest", () => {
}); });
}); });
it("uses the exact uv version locked in uv.lock when it is passed via version-file", () => {
const workingDirectory = createTempProject({
"uv.lock": `version = 1\n\n[[package]]\nname = "uv"\nversion = "0.8.17"\nsource = { registry = "https://pypi.org/simple" }\n`,
});
const request = resolveVersionRequest({
versionFile: path.join(workingDirectory, "uv.lock"),
workingDirectory,
});
expect(request).toEqual({
format: "uv.lock",
source: "version-file",
sourcePath: path.join(workingDirectory, "uv.lock"),
specifier: "0.8.17",
});
});
it("uses requirements.txt when it is passed via version-file", () => { it("uses requirements.txt when it is passed via version-file", () => {
const workingDirectory = createTempProject({ const workingDirectory = createTempProject({
"requirements.txt": "uv==0.6.17\nuvicorn==0.35.0\n", "requirements.txt": "uv==0.6.17\nuvicorn==0.35.0\n",
-4
View File
@@ -52,12 +52,8 @@ inputs:
type: string type: string
manifest-file: manifest-file:
type: string type: string
download-from-astral-mirror:
type: boolean
add-problem-matchers: add-problem-matchers:
type: boolean type: boolean
quiet:
type: boolean
resolution-strategy: resolution-strategy:
type: enum type: enum
allowed-values: allowed-values:
+2 -8
View File
@@ -7,7 +7,7 @@ inputs:
description: "The version of uv to install e.g., `0.5.0` Defaults to the version in pyproject.toml or 'latest'." description: "The version of uv to install e.g., `0.5.0` Defaults to the version in pyproject.toml or 'latest'."
default: "" default: ""
version-file: version-file:
description: "Path to a file containing the version of uv to install, e.g., uv.toml, pyproject.toml, .tool-versions, requirements.txt or uv.lock. Defaults to searching for uv.toml and if not found pyproject.toml." description: "Path to a file containing the version of uv to install. Defaults to searching for uv.toml and if not found pyproject.toml."
default: "" default: ""
python-version: python-version:
description: "The version of Python to set UV_PYTHON to" description: "The version of Python to set UV_PYTHON to"
@@ -61,7 +61,7 @@ inputs:
default: "" default: ""
prune-cache: prune-cache:
description: "Prune cache before saving." description: "Prune cache before saving."
default: "false" default: "true"
cache-python: cache-python:
description: "Upload managed Python installations to the Github Actions cache." description: "Upload managed Python installations to the Github Actions cache."
default: "false" default: "false"
@@ -80,15 +80,9 @@ inputs:
manifest-file: manifest-file:
description: "URL to a custom manifest file in the astral-sh/versions format." description: "URL to a custom manifest file in the astral-sh/versions format."
required: false required: false
download-from-astral-mirror:
description: "Download uv from the Astral mirror instead of directly from GitHub Releases."
default: "true"
add-problem-matchers: add-problem-matchers:
description: "Add problem matchers." description: "Add problem matchers."
default: "true" default: "true"
quiet:
description: "Suppress info-level log output. Only warnings and errors are shown."
default: "false"
resolution-strategy: resolution-strategy:
description: "Resolution strategy to use when resolving version ranges. 'highest' uses the latest compatible version, 'lowest' uses the oldest compatible version." description: "Resolution strategy to use when resolving version ranges. 'highest' uses the latest compatible version, 'lowest' uses the oldest compatible version."
default: "highest" default: "highest"
+2 -2
View File
@@ -1,5 +1,5 @@
{ {
"$schema": "https://biomejs.dev/schemas/2.5.1/schema.json", "$schema": "https://biomejs.dev/schemas/2.4.7/schema.json",
"assist": { "assist": {
"actions": { "actions": {
"source": { "source": {
@@ -34,7 +34,7 @@
"linter": { "linter": {
"enabled": true, "enabled": true,
"rules": { "rules": {
"preset": "recommended" "recommended": true
} }
}, },
"vcs": { "vcs": {
Generated Vendored
+1176 -772
View File
File diff suppressed because it is too large Load Diff
Generated Vendored
+2994 -3904
View File
File diff suppressed because one or more lines are too long
+1620 -2696
View File
File diff suppressed because one or more lines are too long
+7 -18
View File
@@ -6,7 +6,7 @@ This document covers advanced options for configuring which version of uv to ins
```yaml ```yaml
- name: Install the latest version of uv - name: Install the latest version of uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
version: "latest" version: "latest"
``` ```
@@ -15,7 +15,7 @@ This document covers advanced options for configuring which version of uv to ins
```yaml ```yaml
- name: Install a specific version of uv - name: Install a specific version of uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
version: "0.4.4" version: "0.4.4"
``` ```
@@ -28,21 +28,21 @@ to install the latest version that satisfies the range.
```yaml ```yaml
- name: Install a semver range of uv - name: Install a semver range of uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
version: ">=0.4.0" version: ">=0.4.0"
``` ```
```yaml ```yaml
- name: Pinning a minor version of uv - name: Pinning a minor version of uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
version: "0.4.x" version: "0.4.x"
``` ```
```yaml ```yaml
- name: Install a pep440-specifier-satisfying version of uv - name: Install a pep440-specifier-satisfying version of uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
version: ">=0.4.25,<0.5" version: ">=0.4.25,<0.5"
``` ```
@@ -54,7 +54,7 @@ You can change this behavior using the `resolution-strategy` input:
```yaml ```yaml
- name: Install the lowest compatible version of uv - name: Install the lowest compatible version of uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
version: ">=0.4.0" version: ">=0.4.0"
resolution-strategy: "lowest" resolution-strategy: "lowest"
@@ -76,18 +76,7 @@ uv defined as a dependency in `pyproject.toml` or `requirements.txt`.
```yaml ```yaml
- name: Install uv based on the version defined in pyproject.toml - name: Install uv based on the version defined in pyproject.toml
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
version-file: "pyproject.toml" version-file: "pyproject.toml"
``` ```
If uv is locked as a dependency in your `uv.lock`, you can point `version-file` at the
lockfile to install the exact pinned version. This keeps CI runs deterministic and avoids
silently picking up a newer uv until the lockfile is updated.
```yaml
- name: Install uv based on the version locked in uv.lock
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2
with:
version-file: "uv.lock"
```
+26 -25
View File
@@ -23,7 +23,7 @@ The computed cache key is available as the `cache-key` output:
```yaml ```yaml
- name: Setup uv - name: Setup uv
id: setup-uv id: setup-uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
- name: Print cache key - name: Print cache key
@@ -33,8 +33,8 @@ The computed cache key is available as the `cache-key` output:
## Enable caching ## Enable caching
> [!NOTE] > [!NOTE]
> The entire uv cache is uploaded to the GitHub Actions cache by default. To reduce the cache size, > The cache is pruned before it is uploaded to the GitHub Actions cache. This can lead to
> see [Enable cache pruning](#enable-cache-pruning). > a small or empty cache. See [Disable cache pruning](#disable-cache-pruning) for more details.
If you enable caching, the [uv cache](https://docs.astral.sh/uv/concepts/cache/) will be uploaded to If you enable caching, the [uv cache](https://docs.astral.sh/uv/concepts/cache/) will be uploaded to
the GitHub Actions cache. This can speed up runs that reuse the cache by several minutes. the GitHub Actions cache. This can speed up runs that reuse the cache by several minutes.
@@ -50,7 +50,7 @@ You can optionally define a custom cache key suffix.
```yaml ```yaml
- name: Enable caching and define a custom cache key suffix - name: Enable caching and define a custom cache key suffix
id: setup-uv id: setup-uv
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
cache-suffix: "optional-suffix" cache-suffix: "optional-suffix"
@@ -89,7 +89,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml ```yaml
- name: Define a cache dependency glob - name: Define a cache dependency glob
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
cache-dependency-glob: "**/pyproject.toml" cache-dependency-glob: "**/pyproject.toml"
@@ -97,7 +97,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml ```yaml
- name: Define a list of cache dependency globs - name: Define a list of cache dependency globs
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
cache-dependency-glob: | cache-dependency-glob: |
@@ -107,7 +107,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml ```yaml
- name: Define an absolute cache dependency glob - name: Define an absolute cache dependency glob
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
cache-dependency-glob: "/tmp/my-folder/requirements*.txt" cache-dependency-glob: "/tmp/my-folder/requirements*.txt"
@@ -115,7 +115,7 @@ changes. If you use relative paths, they are relative to the working directory.
```yaml ```yaml
- name: Never invalidate the cache - name: Never invalidate the cache
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
cache-dependency-glob: "" cache-dependency-glob: ""
@@ -128,7 +128,7 @@ By default, the cache will be restored.
```yaml ```yaml
- name: Don't restore an existing cache - name: Don't restore an existing cache
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
restore-cache: false restore-cache: false
@@ -142,7 +142,7 @@ By default, the cache will be saved.
```yaml ```yaml
- name: Don't save the cache after the run - name: Don't save the cache after the run
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
save-cache: false save-cache: false
@@ -168,34 +168,35 @@ It defaults to `setup-uv-cache` in the `TMP` dir, `D:\a\_temp\setup-uv-cache` on
```yaml ```yaml
- name: Define a custom uv cache path - name: Define a custom uv cache path
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
cache-local-path: "/path/to/cache" cache-local-path: "/path/to/cache"
``` ```
## Enable cache pruning ## Disable cache pruning
By default, the entire uv cache is persisted across runs. On GitHub-hosted runners, it's typically By default, the uv cache is pruned after every run, removing pre-built wheels, but retaining any
faster to prune the cache before saving it, removing pre-built wheels, but retaining any wheels that wheels that were built from source. On GitHub-hosted runners, it's typically faster to omit those
were built from source. The pre-built wheels are then re-downloaded from the registry on each run. pre-built wheels from the cache (and instead re-download them from the registry on each run).
See the [documentation](https://docs.astral.sh/uv/concepts/cache/#caching-in-continuous-integration) However, on self-hosted or local runners, preserving the cache may be more efficient. See
for more information. the [documentation](https://docs.astral.sh/uv/concepts/cache/#caching-in-continuous-integration) for
more information.
If you want to prune the cache before saving it, enable cache pruning with the `prune-cache` input. If you want to persist the entire cache across runs, disable cache pruning with the `prune-cache`
input.
```yaml ```yaml
- name: Prune the cache before saving it - name: Don't prune the cache before saving it
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
prune-cache: true prune-cache: false
``` ```
## Cache Python installs ## Cache Python installs
By default, the Python install dir (`uv python dir` / `UV_PYTHON_INSTALL_DIR`) is not cached, By default, the Python install dir (`uv python dir` / `UV_PYTHON_INSTALL_DIR`) is not cached,
for the same reason that pruning the dependency cache can improve performance on GitHub-hosted for the same reason that the dependency cache is pruned.
runners.
If you want to cache Python installs along with your dependencies, set the `cache-python` input to `true`. If you want to cache Python installs along with your dependencies, set the `cache-python` input to `true`.
Note that this only caches Python versions that uv actually installs into `UV_PYTHON_INSTALL_DIR` Note that this only caches Python versions that uv actually installs into `UV_PYTHON_INSTALL_DIR`
@@ -204,7 +205,7 @@ To force managed Python installs, set `UV_PYTHON_PREFERENCE=only-managed`.
```yaml ```yaml
- name: Cache Python installs - name: Cache Python installs
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
cache-python: true cache-python: true
@@ -222,7 +223,7 @@ If you want to ignore this, set the `ignore-nothing-to-cache` input to `true`.
```yaml ```yaml
- name: Ignore nothing to cache - name: Ignore nothing to cache
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
enable-cache: true enable-cache: true
ignore-nothing-to-cache: true ignore-nothing-to-cache: true
+3 -3
View File
@@ -10,7 +10,7 @@ are automatically verified by this action. The sha256 hashes can be found on the
```yaml ```yaml
- name: Install a specific version and validate the checksum - name: Install a specific version and validate the checksum
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
version: "0.3.1" version: "0.3.1"
checksum: "e11b01402ab645392c7ad6044db63d37e4fd1e745e015306993b07695ea5f9f8" checksum: "e11b01402ab645392c7ad6044db63d37e4fd1e745e015306993b07695ea5f9f8"
@@ -39,7 +39,7 @@ The `archive_format` field is currently ignored.
```yaml ```yaml
- name: Use a custom manifest file - name: Use a custom manifest file
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
manifest-file: "https://example.com/my-custom-manifest.ndjson" manifest-file: "https://example.com/my-custom-manifest.ndjson"
``` ```
@@ -58,7 +58,7 @@ You can disable this by setting the `add-problem-matchers` input to `false`.
```yaml ```yaml
- name: Install the latest version of uv without problem matchers - name: Install the latest version of uv without problem matchers
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
add-problem-matchers: false add-problem-matchers: false
``` ```
+8 -8
View File
@@ -9,7 +9,7 @@ This allows directly using it in later steps:
```yaml ```yaml
- name: Install the latest version of uv and activate the environment - name: Install the latest version of uv and activate the environment
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
activate-environment: true activate-environment: true
- run: uv pip install pip - run: uv pip install pip
@@ -20,7 +20,7 @@ By default, the venv is created at `.venv` inside the `working-directory`.
You can customize the venv location with `venv-path`, for example to place it in the runner temp directory: You can customize the venv location with `venv-path`, for example to place it in the runner temp directory:
```yaml ```yaml
- uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
activate-environment: true activate-environment: true
venv-path: ${{ runner.temp }}/custom-venv venv-path: ${{ runner.temp }}/custom-venv
@@ -51,7 +51,7 @@ are not sufficient, you can provide a custom GitHub token with the necessary per
```yaml ```yaml
- name: Install the latest version of uv with a custom GitHub token - name: Install the latest version of uv with a custom GitHub token
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
github-token: ${{ secrets.CUSTOM_GITHUB_TOKEN }} github-token: ${{ secrets.CUSTOM_GITHUB_TOKEN }}
``` ```
@@ -69,7 +69,7 @@ input:
```yaml ```yaml
- name: Install the latest version of uv with a custom tool dir - name: Install the latest version of uv with a custom tool dir
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
tool-dir: "/path/to/tool/dir" tool-dir: "/path/to/tool/dir"
``` ```
@@ -88,7 +88,7 @@ If you want to change this behaviour (especially on self-hosted runners) you can
```yaml ```yaml
- name: Install the latest version of uv with a custom tool bin dir - name: Install the latest version of uv with a custom tool bin dir
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
tool-bin-dir: "/path/to/tool-bin/dir" tool-bin-dir: "/path/to/tool-bin/dir"
``` ```
@@ -105,7 +105,7 @@ This action supports expanding the `~` character to the user's home directory fo
```yaml ```yaml
- name: Expand the tilde character - name: Expand the tilde character
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
cache-local-path: "~/path/to/cache" cache-local-path: "~/path/to/cache"
tool-dir: "~/path/to/tool/dir" tool-dir: "~/path/to/tool/dir"
@@ -122,7 +122,7 @@ If you want to ignore this, set the `ignore-empty-workdir` input to `true`.
```yaml ```yaml
- name: Ignore empty workdir - name: Ignore empty workdir
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
ignore-empty-workdir: true ignore-empty-workdir: true
``` ```
@@ -145,7 +145,7 @@ This action sets several environment variables that influence uv's behavior and
```yaml ```yaml
- name: Example using environment variables - name: Example using environment variables
uses: astral-sh/setup-uv@11f9893b081a58869d3b5fccaea48c9e9e46f990 # v8.3.2 uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
with: with:
python-version: "3.12" python-version: "3.12"
tool-dir: "/custom/tool/dir" tool-dir: "/custom/tool/dir"
-81
View File
@@ -1,81 +0,0 @@
# setup-uv Repository Threat Model
## Overview
`setup-uv` is a GitHub Action that installs or reuses `uv`, modifies `PATH` and the environment for later steps, may execute a discovered Python interpreter, may create or clear a virtual environment, and may restore or save caches. It may use `github-token` to authenticate GitHub downloads; it requires no OIDC credential or additional workflow secret.
The consumer runtime is the selected ref's committed action metadata, bundles, and runner-interpreted companion files; source alone is not evidence of shipped behavior. Privileged automation that generates, updates, or publishes those artifacts is also in scope.
The assets are job credentials; integrity of installed executables, interpreter, environment, checkout, runner, artifacts, and caches; isolation between jobs sharing caches or persistent runners; integrity of published action refs; and workflow compute/storage availability.
Material failures are unauthorized executable selection, credential disclosure, premature execution of lower-authority content, filesystem escape or destructive path use, cross-authority cache/runner persistence, and unauthorized publication.
## Threat Model, Trust Boundaries, and Assumptions
### Authority and trust boundaries
| Actor or input | Trust decision |
|---|---|
| Maintainers, repository/configuration administrators, and GitHub infrastructure | Trusted roots for source, bundles, workflows, refs, rulesets, environments, runner protocol, hosted isolation, and cache service. A lower-authority path into these roots is in scope; their compromise alone is not a repository bug. |
| Consumer workflow authors and runner operators | Control the action ref, trigger, runner, permissions, secrets, proxy, environment, inputs, paths, globs, and custom sources. These are trusted choices unless derived from lower-authority event data. Selecting a custom manifest delegates metadata and executable authority; selecting a path authorizes normal operations on it and intended referents. |
| Selected checkout, project authors, and pull-request contributors | The consumer delegates project/version files, interpreter discovery state, virtual environments, symlinks, cache inputs, and code execution within `setup-uv`'s process environment. Checkout-controlled behavior is trusted unless it overrides an explicit workflow choice or crosses an independent cache, runner, remote, or publication boundary. |
| Remote metadata and artifacts | Default official endpoints, TLS roots, and an operator proxy are trusted mutable authorities. A custom manifest authorizes its URLs and hashes; a hash supplied by that same authority detects corruption, not malice. |
| Cache and runner-state producers/consumers | Same-principal state is trusted by default. Integrity attacks require a lower-authority producer and higher-authority consumer. Confidentiality can flow the opposite way because lower-authority refs may read eligible higher-authority caches. Shared self-hosted state creates a boundary only when principals and authority differ. |
| GitHub-managed automation | Dependency, coding-agent, and review workflows may exist outside the committed tree. Treat them as external principals and obtain their effective trigger, actor, token, environment, ref, and write/secret authority from live evidence. |
### Assumptions
- Running the selected `uv` and checkout-selected Python interpreters is intended. Project execution is out of scope unless it bypasses an explicit workflow choice or crosses an independent cache, runner, remote, or publication boundary.
- Mutable official manifests, ranges, `latest`, and unprotected refs are not attacker control. A protected ref or independent checksum matters only if the selected bundle actually enforces it.
- Same-user changes to paths, environment, proxies, or tool/cache state are not separate attacks. Demonstrate a cross-principal or lower-to-higher boundary.
- Content merged through a trust path that can also merge executable code is not a lower-authority source; require a narrower writer or post-review mutation path.
- Running `setup-uv` on an untrusted checkout with higher authority is a consumer trust decision; checkout-selected code may inherit the action environment.
- Authorized paths include expected symlink/junction referents. Absolute paths and paths outside the workspace are supported; an escape requires independent control crossing an unauthorized boundary.
- Hosted runners are assumed ephemeral and isolated. Persistence or hostile co-tenancy on self-hosted runners must be demonstrated.
- Branch/tag rules, environments, token defaults, cache visibility, fork policy, dynamic workflows, and runner allocation are external state. Re-query required approvals/checks, bypass actors, tag movement, deployment reviewers/principals, release targets, and effective permissions for each attack path.
- Web-application classes such as sessions, CSRF, XSS, SQL injection, and tenant isolation are not applicable.
### Security invariants
1. **Published runtime:** review `action.yml`, committed `dist/*.cjs`, and runner-interpreted shipped files; source-only fixes do not protect consumers.
2. **Executable identity:** precedence is workflow version, version file, project configuration, then `latest`. Manifest authority, platform, variant, URL, checksum, mirror fallback, extraction, and cache placement must bind the intended artifact. A tool-cache hit bypasses download validation and depends on cache provenance.
3. **Credential recipients:** tokens and URL credentials may reach only workflow-authorized origins, redirects, paths, and logs. Metadata authority does not imply token-recipient authority.
4. **Executable boundaries:** checkout-selected interpreters are authorized by default. Explicit workflow selections must win, and independent cache, runner, or remote state must not substitute executables or gain additional authority.
5. **Paths and action channels:** path/environment changes, virtual-environment clearing, outputs, state, and problem matchers must affect only authorized targets and keep untrusted values as data.
6. **Cache boundaries:** keys, scope, restore paths, and executable content must prevent lower-to-higher poisoning; cache contents and post-action path re-resolution must prevent higher-to-lower disclosure, destructive pruning, or persistence.
7. **Workflow and release authority:** unreviewed code or mutable tooling must not acquire write, secret, OIDC, artifact, deployment, tag, or publication authority. Only the intended reviewed bundles and commit may be released.
8. **Availability:** independently controlled manifests, archives, globs, traversal, and caches must stay within the accepted one-job resource-failure model.
### Finding gate
Before reporting, identify the attacker and victim principals; exact controlled input; scanned action and checkout refs; runtime reachability in committed bundles; effective token, secrets/OIDC, environment gates, cache scope, and runner persistence; applicable defaults and opt-ins; validation performed or skipped; declared trust roots; baseline versus incremental capability; and concrete impact. Reproduce platform-specific behavior and distinguish the scanned ref from other versions.
A report must demonstrate independent attacker control, a violated guarantee, committed-runtime reachability, incremental capability, and practical impact; otherwise it is not a security finding and should not be reported as one.
## Attack Surface, Mitigations, and Attacker Stories
| Surface | Security-relevant behavior and controls | Reportable attacker story |
|---|---|---|
| Published action and build/release supply chain | Consumers execute committed bundles and embedded dependencies. Verify source/bundle alignment, lockfile integrity, dependency-install policy, reproducible/generated-diff checks, immutable action pins, branch enforcement, and publication target checks. | A lower-authority contributor or dependency changes shipped code, or release automation publishes a different commit, by bypassing an effective review, branch, or release control. |
| Version, manifest, proxy, and network selection | Project files may select an official version by documented precedence. Custom manifests may select URLs, hashes, variants, and platforms and may reach arbitrary network locations. Parsing should reject malformed, ambiguous, unsupported, or incorrectly typed records; verify HTTPS, time/size bounds, proxy behavior, and selected-ref defaults. | Lower-authority event/project data violates a promised fixed version, escapes the selected manifest, probes runner-only services, causes material resource use, selects attacker bytes, or redirects later credentials. Operator selection of a custom authority is not itself a finding. |
| Artifact URL, token, checksum, extraction, and tool cache | Mirror fallback must preserve identity and checksum policy. Origin gating should restrict tokens; redirect handling should strip authorization across unauthorized hosts and reject downgrade. Verify checksum precedence and reject missing/empty hashes when policy requires validation. Independent hashes must precede extraction. Native helpers come from `PATH`; tool-cache hits skip network/hash validation. | An attacker receives a usable token outside delegated authority, bypasses an independent pin, exploits archive/link traversal, substitutes the cached executable, or poisons shared tool state later executed with higher authority. Same-authority manifest hashes and same-user cache changes do not establish the boundary. |
| Interpreter, PATH, virtual environment, and action channels | Checkout-selected interpreters, virtual environments, paths, symlinks, and helpers are delegated project authority. Explicit workflow choices must bind; the action also changes later-step paths/environment, emits state/outputs, invokes native helpers, and consumes cache/runner state. | Independent cache, runner, or remote content substitutes an executable; an explicit workflow choice is bypassed; or action channels cross an authority boundary. Same-checkout interpreter, path, and helper effects are not findings. |
| GitHub uv/Python caches and post action | Cache keys should partition platform, interpreter, dependency, and policy state and restore without unsafe fallback. Determine cache defaults, visibility, and the exact hit/miss path from the selected ref and GitHub policy; an exact hit may suppress post save/prune. Post processing re-reads inputs/config/environment and may save re-resolved uv or Python paths. | A lower producer supplies executable content to a higher consumer; a higher producer exposes private data to a lower cache reader; or a later successful step retargets a cache miss toward sensitive files, destructive pruning, or cross-job persistence. Existing equal-authority code with the same secrets often gains no new confidentiality. |
| CI, updater, dynamic automation, and release workflows | PR workflows intentionally execute contributor code. Verify effective permissions, fork behavior, credential persistence, mutable tooling, security-upload authority, and whether checks are required. Updaters convert remote data into source under write authority. Distinguish ruleset-required deployment from human review present only in a workflow DAG. | Unreviewed code gains write/secret/OIDC/artifact authority; remote metadata becomes executable generated source; a dynamic workflow has unexpected authority; or an actor satisfies a deployment/tag rule without the intended review and publishes a malicious ref. |
| Availability and logging | Manifests, version enumeration, archives, globs, hashing, caches, and remote strings can consume resources or influence logs. Verify size/count/expansion bounds, timeouts, retries, top-level error handling, and that parsing never executes data. | Independently controlled input causes reliable material workflow cost, disk/memory exhaustion, or meaningful log/output manipulation. A bounded one-job failure or operator-selected broad input is usually Low or correctness. |
| Lower-priority classes | Shell injection is constrained where child execution uses argv, but workflow shell blocks still require quoting review. Prototype pollution requires a dangerous merge/sink. Secret-shaped strings require proof of a genuine usable secret. Documentation drift, range surprises, malformed trusted config, and test-only code normally lack a security boundary. | Report only when a concrete lower-authority value reaches an execution, credential, persistent-state, publication, or material-availability sink. |
## Severity Calibration (Critical, High, Medium, Low)
Severity follows the complete attack graph and incremental capability, not the presence of words such as token, checksum, cache, manifest, archive, Python, PATH, release, or OIDC.
| Severity | Threshold | Representative examples |
|---|---|---|
| **Critical** | A low-prerequisite remote/lower-authority attacker compromises default distribution or installation across many consumers, publishes trusted malicious action artifacts, or gains broad credentials/runner control under safe defaults without first compromising a declared trust root. | Bypass an effective hash/origin control to distribute an automatically executed malicious binary at scale; reach publication authority to ship malicious bundles or move trusted refs without required approval; exploit default-accepted archive content for host overwrite or cross-job execution across hosted runners. |
| **High** | A demonstrated lower-authority input crosses an execution, confidentiality, integrity, or persistence boundary in a privileged job and gains substantial capability. | Independent shared-state interpreter substitution in a write/OIDC release job; shared cache poisoning later executed with secrets; high-value cache disclosure to an untrusted ref; usable write-token disclosure; independent-pin bypass; archive/cache escape into sensitive state. |
| **Medium** | A real but constrained crossing causes limited credential/filesystem impact, reliable remote denial of service, scoped persistence, or premature execution in a realistic uncommon configuration. | Limited executable substitution from independent cache/runner state in a read-only job; same-repository cache confusion or disclosure; reliable hosted-runner exhaustion; disclosure of a usable read-only private token; output manipulation without publication or high-value credentials. |
| **Low** | A genuine weak boundary causes narrow disclosure, log/annotation spoofing, defense-in-depth weakness, exotic cache aliasing without a privileged consumer, or limited waste. | Confusing logs with no execution effect; bounded job failure; limited overwrite of nonexecuted cache data; disclosure of a path/URL without private data or follow-on capability. |
Trust-root compromise may have Critical impact but is not a repository Critical without a lower-authority path into that root or an independent control that should have survived. High requires exact trigger, refs, effective authority, sink, and committed runtime; it cannot rely only on a trusted operator choosing malicious inputs, same-user state changes, or code already intentionally executed with equal authority. A separate privileged consumer, broad secret, persistent trusted state, publication path, or cross-repository boundary can raise Medium to High.
Normally non-reportable without additional evidence: expected mutability of ranges, `latest`, official/custom sources, or unprotected refs; documented project version selection; checkout-selected interpreters, paths, virtual environments, symlinks, and helpers; deliberate operator selection of manifests, proxies, checksums, or paths; same-principal cache/path changes; requested `uv` or dependency execution; trusted-runner `PATH` lookup; test/developer-only code without a shipped or privileged-workflow path; behavior fixed in the scanned ref; and correctness/compatibility/documentation issues without incremental confidentiality, integrity, persistence, or availability impact.
+1305 -1413
View File
File diff suppressed because it is too large Load Diff
+12 -12
View File
@@ -28,26 +28,26 @@
"author": "@eifinger", "author": "@eifinger",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@actions/cache": "^6.1.0", "@actions/cache": "^6.0.0",
"@actions/core": "^3.0.0", "@actions/core": "^3.0.0",
"@actions/exec": "^3.0.0", "@actions/exec": "^3.0.0",
"@actions/glob": "^0.6.1", "@actions/glob": "^0.6.1",
"@actions/io": "^3.0.2", "@actions/io": "^3.0.2",
"@actions/tool-cache": "^4.0.0", "@actions/tool-cache": "^4.0.0",
"@renovatebot/pep440": "^5.0.0", "@renovatebot/pep440": "^4.2.2",
"smol-toml": "^1.7.0", "smol-toml": "^1.6.0",
"undici": "^8.3.0" "undici": "^7.24.2"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^2.5.1", "@biomejs/biome": "^2.4.7",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",
"@types/node": "^26.1.1", "@types/node": "^25.5.0",
"@types/semver": "^7.7.1", "@types/semver": "^7.7.1",
"@vercel/ncc": "^0.44.1", "@vercel/ncc": "^0.38.4",
"esbuild": "^0.28.1", "esbuild": "^0.27.4",
"jest": "^30.4.2", "jest": "^30.3.0",
"js-yaml": "^5.2.1", "js-yaml": "^4.1.1",
"ts-jest": "^29.4.11", "ts-jest": "^29.4.6",
"typescript": "^6.0.3" "typescript": "^5.9.3"
} }
} }
+9 -12
View File
@@ -2,7 +2,6 @@ import * as cache from "@actions/cache";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { hashFiles } from "../hash/hash-files"; import { hashFiles } from "../hash/hash-files";
import type { SetupInputs } from "../utils/inputs"; import type { SetupInputs } from "../utils/inputs";
import * as log from "../utils/logging";
import { getArch, getOSNameVersion, getPlatform } from "../utils/platforms"; import { getArch, getOSNameVersion, getPlatform } from "../utils/platforms";
export const STATE_CACHE_KEY = "cache-key"; export const STATE_CACHE_KEY = "cache-key";
@@ -20,7 +19,7 @@ export async function restoreCache(
core.setOutput("cache-key", cacheKey); core.setOutput("cache-key", cacheKey);
if (!inputs.restoreCache) { if (!inputs.restoreCache) {
log.info("restore-cache is false. Skipping restore cache step."); core.info("restore-cache is false. Skipping restore cache step.");
core.setOutput("python-cache-hit", false); core.setOutput("python-cache-hit", false);
return; return;
} }
@@ -56,7 +55,7 @@ async function restoreCacheFromKey(
stateKey: string, stateKey: string,
outputKey: string, outputKey: string,
): Promise<void> { ): Promise<void> {
log.info( core.info(
`Trying to restore cache from GitHub Actions cache with key: ${cacheKey}`, `Trying to restore cache from GitHub Actions cache with key: ${cacheKey}`,
); );
let matchedKey: string | undefined; let matchedKey: string | undefined;
@@ -64,7 +63,7 @@ async function restoreCacheFromKey(
matchedKey = await cache.restoreCache([cachePath], cacheKey); matchedKey = await cache.restoreCache([cachePath], cacheKey);
} catch (err) { } catch (err) {
const message = (err as Error).message; const message = (err as Error).message;
log.warning(message); core.warning(message);
core.setOutput(outputKey, false); core.setOutput(outputKey, false);
return; return;
} }
@@ -78,7 +77,7 @@ async function computeKeys(
): Promise<string> { ): Promise<string> {
let cacheDependencyPathHash = "-"; let cacheDependencyPathHash = "-";
if (inputs.cacheDependencyGlob !== "") { if (inputs.cacheDependencyGlob !== "") {
log.info( core.info(
`Searching files using cache dependency glob: ${inputs.cacheDependencyGlob.split("\n").join(",")}`, `Searching files using cache dependency glob: ${inputs.cacheDependencyGlob.split("\n").join(",")}`,
); );
cacheDependencyPathHash += await hashFiles( cacheDependencyPathHash += await hashFiles(
@@ -86,7 +85,7 @@ async function computeKeys(
true, true,
); );
if (cacheDependencyPathHash === "-") { if (cacheDependencyPathHash === "-") {
log.warning( core.warning(
`No file matched to [${inputs.cacheDependencyGlob.split("\n").join(",")}]. The cache will never get invalidated. Make sure you have checked out the target repository and configured the cache-dependency-glob input correctly.`, `No file matched to [${inputs.cacheDependencyGlob.split("\n").join(",")}]. The cache will never get invalidated. Make sure you have checked out the target repository and configured the cache-dependency-glob input correctly.`,
); );
} }
@@ -94,10 +93,8 @@ async function computeKeys(
if (cacheDependencyPathHash === "-") { if (cacheDependencyPathHash === "-") {
cacheDependencyPathHash = "-no-dependency-glob"; cacheDependencyPathHash = "-no-dependency-glob";
} }
const suffix = inputs.cacheSuffix const suffix = inputs.cacheSuffix ? `-${inputs.cacheSuffix}` : "";
? `-${encodeURIComponent(inputs.cacheSuffix)}` const version = pythonVersion ?? "unknown";
: "";
const version = encodeURIComponent(pythonVersion ?? "unknown");
const platform = await getPlatform(); const platform = await getPlatform();
const osNameVersion = getOSNameVersion(); const osNameVersion = getOSNameVersion();
const pruned = inputs.pruneCache ? "-pruned" : ""; const pruned = inputs.pruneCache ? "-pruned" : "";
@@ -112,12 +109,12 @@ function handleMatchResult(
outputKey: string, outputKey: string,
): void { ): void {
if (!matchedKey) { if (!matchedKey) {
log.info(`No GitHub Actions cache found for key: ${primaryKey}`); core.info(`No GitHub Actions cache found for key: ${primaryKey}`);
core.setOutput(outputKey, false); core.setOutput(outputKey, false);
return; return;
} }
core.saveState(stateKey, matchedKey); core.saveState(stateKey, matchedKey);
log.info(`cache restored from GitHub Actions cache with key: ${matchedKey}`); core.info(`cache restored from GitHub Actions cache with key: ${matchedKey}`);
core.setOutput(outputKey, true); core.setOutput(outputKey, true);
} }
-468
View File
@@ -1,473 +1,5 @@
// AUTOGENERATED_DO_NOT_EDIT // AUTOGENERATED_DO_NOT_EDIT
export const KNOWN_CHECKSUMS: { [key: string]: string } = { export const KNOWN_CHECKSUMS: { [key: string]: string } = {
"aarch64-apple-darwin-0.11.30":
"9bed3567d496d8dab84ecf7a1247551ac94ef1baaebb7b65df008dd93e9dc357",
"aarch64-pc-windows-msvc-0.11.30":
"0edc44e7f23668bce7985facd96b2fe04a4d8ea8edfc7e53294afd8993e960fc",
"aarch64-unknown-linux-gnu-0.11.30":
"8c11d90f5f66d232930cf8ae3a085c39877690d409e10878234802b028b20e2a",
"aarch64-unknown-linux-musl-0.11.30":
"7562a40e4e08b1bfd566bd6aeca55a16ee5ac45211554543e8cdb3c395b47416",
"arm-unknown-linux-musleabihf-0.11.30":
"58967610a6f14f2785e3be3b1b47553b5ff92c3d51bc0f1d9d90d83dc224f0db",
"armv7-unknown-linux-gnueabihf-0.11.30":
"8e2d2ab63a37af47c1b4126c5871ee191b983a10670af91d8ef0bee198292931",
"armv7-unknown-linux-musleabihf-0.11.30":
"33b515887e04c79d4d5de5967b8278b9dac1853cc8d657bf77052bab16a6629f",
"i686-pc-windows-msvc-0.11.30":
"8ceaa74889e30d64adf55d7ad7acce6ed101fb036155bf2d7613715c9d9533c1",
"i686-unknown-linux-gnu-0.11.30":
"e53221602d83b03c63a5e262bfcbf0f5f6e5972e93847498418bdb42005f04e7",
"i686-unknown-linux-musl-0.11.30":
"f66898852a6a745a8c0ab0a26e046857ee77c9f24e4a3b64cb078753490b3978",
"powerpc64le-unknown-linux-gnu-0.11.30":
"81a71fa495239c372b3b0c1bb2509167da00cf307323a2d2a39cbc92f0c6d5d1",
"riscv64gc-unknown-linux-gnu-0.11.30":
"e2d65822d8f668f6d055679dba4937e386764b9561f9d1269d3bc9e7140f7491",
"riscv64gc-unknown-linux-musl-0.11.30":
"3d75e26ace5e679e5de26106a71ee775af5c7f7ad872cd04f33b4d53c523a707",
"s390x-unknown-linux-gnu-0.11.30":
"a30f253aca1291dbcdbb7f76c754235f268ff84d106a595f7d1abf337ab22ac1",
"x86_64-apple-darwin-0.11.30":
"ce285fbbfbe294b1e1bc6c87c8b59d9622b85383b88b2b132a2df5c73e83d7c1",
"x86_64-pc-windows-msvc-0.11.30":
"be8d78c992312212e5cc05e9f9de3fa996db73b7c86a186dfb9231eb9f91d33e",
"x86_64-unknown-linux-gnu-0.11.30":
"04bc7d180d6138bf6dc08387acf507a823f397a98fea55da36b0ccc7fbce3b68",
"x86_64-unknown-linux-musl-0.11.30":
"023fdd3b59fccfb67b365c69fb34182aaaed1d685d367a57571e3c1468a4c70c",
"aarch64-apple-darwin-0.11.29":
"61c04acc52a33ef0f331e494bdfbedcdb6c26c6970c022ed3699e5860f8930e3",
"aarch64-pc-windows-msvc-0.11.29":
"55b597ae81bc29531a7c352a1431a8a73cc2755d7a5b9ec454580cbe02e5154f",
"aarch64-unknown-linux-gnu-0.11.29":
"94500fb064ae3c971a873cba64d94694c50677e0a4dbf78735c80509e7429919",
"aarch64-unknown-linux-musl-0.11.29":
"593d79a797ece3f1dfaaf3e0a973263422a135d9262c7dbc6cd75d9c11acc0b4",
"arm-unknown-linux-musleabihf-0.11.29":
"b160d7eb7dc45af378b3c9dd2cd6b07d64c65b509f2da673434e8e5dc996e5b1",
"armv7-unknown-linux-gnueabihf-0.11.29":
"7b5717ae304fbb1e94104699fb8c08b32d1537fedc90dd8fcf87768d818951ed",
"armv7-unknown-linux-musleabihf-0.11.29":
"bfd05286a80b39bd4708bbbb4450fe09f07fea86eea60d31ea2de0b5e816ef54",
"i686-pc-windows-msvc-0.11.29":
"c173af6f6e125d65214b55d6b75fcf1a32d5e1c3a656938740fe0b2379cd4bcc",
"i686-unknown-linux-gnu-0.11.29":
"c62af324951ae6f31f9453280e077176c96b6e31897d8133c00411a91a20878f",
"i686-unknown-linux-musl-0.11.29":
"368dd75d030cae0512631ba0d50604e8471275ec2d9f02836edb91e4c82ad36a",
"powerpc64le-unknown-linux-gnu-0.11.29":
"641b15637de9fedc7e738b0e4716b2233c792ba6dca722ba0484381749e1a9fd",
"riscv64gc-unknown-linux-gnu-0.11.29":
"5c1229cc0309ebc6872ee847b8fc75564b3b7688edeb09e19427203e2dbc0ea7",
"riscv64gc-unknown-linux-musl-0.11.29":
"d4708d913ba88fad9fe1d81da3e13aecadde7a3190cc095a7f2af2fa5c8caa8f",
"s390x-unknown-linux-gnu-0.11.29":
"fa029183f550a3b00b89c06529fabfa11e0c69c097ccd397a8e7eae46f397348",
"x86_64-apple-darwin-0.11.29":
"c4c4de482da9ccdd076dc4fb5cfe7b740609029385c72f58606be3153602387d",
"x86_64-pc-windows-msvc-0.11.29":
"a047d55651bc3e0ca24595b25ec4cfcb10f9dca9fb56514e661269b37d4fae68",
"x86_64-unknown-linux-gnu-0.11.29":
"04f8b82f5d47f0512dcd32c67a4a6f16a0ea27c81537c338fd0ad6b23cebe829",
"x86_64-unknown-linux-musl-0.11.29":
"46711858adb2a3acaa9cee00f9060688ad1fd5706aecc005b96a6a7f285a00b7",
"aarch64-apple-darwin-0.11.28":
"33540eb7c883ab857eff79bd5ac2aa31fe27b595abecb4a9c003a2c998447232",
"aarch64-pc-windows-msvc-0.11.28":
"3248109afad3ec59baad299d324ff53de17e2d9a3b3e21580ffd26744b11e036",
"aarch64-unknown-linux-gnu-0.11.28":
"03e9fe0a81b0718d0bc84625de3885df6cc3f89a8b6af6121d6b9f6113fb6533",
"aarch64-unknown-linux-musl-0.11.28":
"da10cdfa7d92212b7acb62021a0fd61bcf8580c58c3632ec915d10c3a1a7906b",
"arm-unknown-linux-musleabihf-0.11.28":
"955f9697d4781cf4dfc62d9442a49f3e57861f19405a981ffe9fdc0d18e0a31a",
"armv7-unknown-linux-gnueabihf-0.11.28":
"b7cddaad27cd531096e88e64de863f7e37c6fedde11c97595060e6503544d120",
"armv7-unknown-linux-musleabihf-0.11.28":
"eb00e0f246d278133df6a55b5d3534a49f051a7dfc276f640b66504c091167f6",
"i686-pc-windows-msvc-0.11.28":
"01975deb06072edfc1f18435c25737d8d378dc849b918cd580768ab3b7641e85",
"i686-unknown-linux-gnu-0.11.28":
"f8d01e918dd4d5eabdd4caee78e8adbc3993a70f3b82395fa6b23fcf791925d9",
"i686-unknown-linux-musl-0.11.28":
"c869415e44e13000d9daae15cec7bcfaf0735915a8d6e66402d459f6bed35710",
"powerpc64le-unknown-linux-gnu-0.11.28":
"b634b6f26a036d112914c2d6d7ae1944bf8f4a6dfed6e19a22b35e5d91f168fb",
"riscv64gc-unknown-linux-gnu-0.11.28":
"be7473dc29ee88bd260e30642faa5ba6dccd7d4493012077873b293c377ab546",
"riscv64gc-unknown-linux-musl-0.11.28":
"4a9e553d8a62c42600d5c2874b7c2f728c03d4310d72ed7351edbee8ed6a71b4",
"s390x-unknown-linux-gnu-0.11.28":
"f5ed9d2aed566e11ffc3f1f7ab5b4049dfbfcb04c326ce2ed5bbf42069d62b27",
"x86_64-apple-darwin-0.11.28":
"2ad79983127ffca7d77b77ce6a24278d7e4f7b817a1acf72fea5f8124b4aac5e",
"x86_64-pc-windows-msvc-0.11.28":
"0a23463216d09c6a72ff80ef5dc5a795f07dc1575cb84d24596c2f124a441b7b",
"x86_64-unknown-linux-gnu-0.11.28":
"e490a6464492183c5d4534a5527fb4440f7f2bb2f228162ad7e4afe076dc0224",
"x86_64-unknown-linux-musl-0.11.28":
"f02146b371c35c287d860f003ece7345c86e358a3fd70a9b63700cd141ee7fb4",
"aarch64-apple-darwin-0.11.27":
"34e63cc0de0aebbc8d424767c588c31b685479f045f9ced9e5ef43ff9e0e8d63",
"aarch64-pc-windows-msvc-0.11.27":
"7566a80fe96ee84e6938621a1b704f44b0db546672bf43025905784b2507b7fe",
"aarch64-unknown-linux-gnu-0.11.27":
"321580b9a7069d0cdbd8db9482a5fb62b4f1285110f847746e3b495408e3a08c",
"aarch64-unknown-linux-musl-0.11.27":
"b0b1909a7e5caf2ec0cbe2649f5171050c26d85efb65d9d4de2cfe754dc14ea3",
"arm-unknown-linux-musleabihf-0.11.27":
"d2a165d17814b88d0190ae69bca9877261d26cac79aba3d27d343a56a7ac47ba",
"armv7-unknown-linux-gnueabihf-0.11.27":
"b54d68117ca6f9ba451537acf89ed5c6723bcd08a2d33e21a543659fce31cfd8",
"armv7-unknown-linux-musleabihf-0.11.27":
"ecdc47f7fbb3eed8081f16ea24d61e9ae562c3a0286eb75a9633ebd75f6ff0ab",
"i686-pc-windows-msvc-0.11.27":
"479fa122a5a332b1bb09a877d7fa1cc2215a951d2af0af966336a640ae9cc568",
"i686-unknown-linux-gnu-0.11.27":
"9add1a8c71bbbbe9058c56ab37c7a423baa4dcdcda64194a4e4f588f323f38da",
"i686-unknown-linux-musl-0.11.27":
"377d0e1a5a34fc66e7e060adefaf4f141882460e84e76e7a8e9943f8d6e933d8",
"powerpc64le-unknown-linux-gnu-0.11.27":
"f7673c8308b4f7df79b4dd3771dc43d4140d44439f500f82168121bda1eb12f4",
"riscv64gc-unknown-linux-gnu-0.11.27":
"ad677746993287a092e3d32748b24751098638bc1188d4340f8d47583f0ac1ee",
"riscv64gc-unknown-linux-musl-0.11.27":
"7fee728248d487de1b76aa31882319fec8ee117d5128cfd1e1fd78ab2f77184b",
"s390x-unknown-linux-gnu-0.11.27":
"2a984c393b494bfef78f58770e09295fbebe33ec88eede32f6b59df2a8ac2995",
"x86_64-apple-darwin-0.11.27":
"9f00047455b2a9e81f282297fca39cdd6cd5761a6b0ce75e2d7698744c59e1af",
"x86_64-pc-windows-msvc-0.11.27":
"b7e32288ce0e289dbe94d2cac7adbb008f74f0e038542a2d9969dd50eb7056ee",
"x86_64-unknown-linux-gnu-0.11.27":
"0f4088a04ac92e4c52b4b76759d227a1047355e0ce1dd57cd738a6dec5966bd9",
"x86_64-unknown-linux-musl-0.11.27":
"5d5594af1530c7c31e46a8cc0a35ceb4d28f3890049efe2149ac53c9ad121493",
"aarch64-apple-darwin-0.11.26":
"8f7fbf1708399b921857bce71e1d60f0d3ccf52a30caebc1c1a2f175dce13ab6",
"aarch64-pc-windows-msvc-0.11.26":
"98246149741f558e25e45ecf2b0b20f34de0634269f2bf0dcb4012d4b6ba289a",
"aarch64-unknown-linux-gnu-0.11.26":
"befa1a59c91e96eb601b0fd9a97c03dd666f17baba644b2b4db9c59a767e387e",
"aarch64-unknown-linux-musl-0.11.26":
"47418cfdb34b1ca42e503da72631ac8c475602e2411ac6c39aa84c2373fe6324",
"arm-unknown-linux-musleabihf-0.11.26":
"400ebbca4062f7960cbdb2359cd28741dc9a1fab2656abb9ee05fb525e3f1e12",
"armv7-unknown-linux-gnueabihf-0.11.26":
"e1a6e135f68c237f969f2acb5277ef2691dec582e99d9f6c237c21583bd26f7b",
"armv7-unknown-linux-musleabihf-0.11.26":
"3b4453d821588f7fafe622a099db4b7f55a668bec79db73411414ffc24b8c45d",
"i686-pc-windows-msvc-0.11.26":
"6e19e7ef0ccacfeea7edeb0a7be951d31148b49f5170c1770c58db312fe443f8",
"i686-unknown-linux-gnu-0.11.26":
"65cc2d8719febecd1bc2fd3ea437c72317ac54de96e54de0a73344e150982478",
"i686-unknown-linux-musl-0.11.26":
"ef5d5c982dfd4d6b5aa107c33b882b978ccccf1487500ae2adbc1d3665ea7e04",
"powerpc64le-unknown-linux-gnu-0.11.26":
"095f7cfd814495719244897f96f1c35f10369bcd9237276c63cfed824353f505",
"riscv64gc-unknown-linux-gnu-0.11.26":
"253d728a2660006e4aadee627f5d9f3ca2a874559b5fc716a5023965e58092bf",
"riscv64gc-unknown-linux-musl-0.11.26":
"4ef67e6bb961583eb4ee7ce61b3c98bcede9e51b771b4b4967719d0fb6aa3cbd",
"s390x-unknown-linux-gnu-0.11.26":
"34f514ed6ecbf33a3d90346c8bf3074954dd0c76340f5765b88577daa05d307b",
"x86_64-apple-darwin-0.11.26":
"922b460202707dd5f4ccacbadbe7f6a546cc46e82a99bf50ca99a7977a78eddd",
"x86_64-pc-windows-msvc-0.11.26":
"4e1278ede866be6c0bf32d2f466cc6de7a9fb399ecf20c9ce2d186e52424be47",
"x86_64-unknown-linux-gnu-0.11.26":
"6426a73c3837e6e2483ee344cbc00f36394d179afcba6183cb77437e67db4af0",
"x86_64-unknown-linux-musl-0.11.26":
"62bf1a53501adf4083224b69b33737450ac516935f5a5e483e9dfaf2665084de",
"aarch64-apple-darwin-0.11.25":
"5fc334bb25d19806262efd1f6e7d380155c7e817d89bf426df4ba7ae873c9471",
"aarch64-pc-windows-msvc-0.11.25":
"40d65c29c4d97db6a0993df665d3727700bb799b3618992ce9a4dc533c6d1a31",
"aarch64-unknown-linux-gnu-0.11.25":
"e0e9d73f74e06a7dcd53910d5962146ab48f0af9c92cc8df33a37baa0121014d",
"aarch64-unknown-linux-musl-0.11.25":
"3d9c9a95f1868ff20ac880712f3a337d710ea4b65f135184b7a35635d1acbf84",
"arm-unknown-linux-musleabihf-0.11.25":
"50ef6368eebaa9f31d8e8685a91689666c7bae86a1bd9a72b7a505f752084ef9",
"armv7-unknown-linux-gnueabihf-0.11.25":
"eb60b2931d2461022e1675a7f6c08078b1e47150edb2f7728fe117563a5ebc99",
"armv7-unknown-linux-musleabihf-0.11.25":
"50f6f10ca38e4628998c17de24c2780f429d9b354db98b1832c130b7394e8ec4",
"i686-pc-windows-msvc-0.11.25":
"91a87e8a7ea2d5e9451b9bd8b1e9fa490542311ac43e80a9115abbd231c417fb",
"i686-unknown-linux-gnu-0.11.25":
"1b0f2ceee8dbe718eb1e14d7eccf67534d2f72698f4f85e70cf8a5055412b5d3",
"i686-unknown-linux-musl-0.11.25":
"e650be31af3fd31f5e2236a47daab0899070a66a732026d140461f1614fbe786",
"powerpc64le-unknown-linux-gnu-0.11.25":
"fbf67c8beccc718b9de478d7e74d605fe9c2e259c4398f95c9df7680da710af1",
"riscv64gc-unknown-linux-gnu-0.11.25":
"29e74713e89242c6bf0971b28e904836298a04fb7e0d83262c7bd5aff695ae33",
"riscv64gc-unknown-linux-musl-0.11.25":
"90c5a720dd9ddf32de8511c79fc83bc69e38795a94b5b49fcb2d010f96b1374d",
"s390x-unknown-linux-gnu-0.11.25":
"3d508ed6e66898ce80aa668cb7d6ec4ddb4d30440fcfbd409365d3d0e4afa75c",
"x86_64-apple-darwin-0.11.25":
"65ff85b33212f75d34d7c0f0724aba9a742c74f62559f67dc0d6c543dc2fc52f",
"x86_64-pc-windows-msvc-0.11.25":
"15bfd1423b7eaa7aae949922d4712ebaac2bb44a81af64ab59bbe007090cb0d0",
"x86_64-unknown-linux-gnu-0.11.25":
"1db18b5e76fa645a7f3865773139bdec8e2d46adbdbb35e7410b34fa8015ccd2",
"x86_64-unknown-linux-musl-0.11.25":
"7195cfcc1785d0c559d4682ad9109f26664b8db4d828d42d66078ef454061891",
"aarch64-apple-darwin-0.11.24":
"7578c6087c5cd76981732b1f5d126248101faebdf81016ba780a65ce03653cdf",
"aarch64-pc-windows-msvc-0.11.24":
"51bcf8051dcc2075fba9136ece0f229205a6ef813e68e5709c6eeb18802afed5",
"aarch64-unknown-linux-gnu-0.11.24":
"e22c66d36a0098b17cff80a8647e0b8c58202af899d4e9eb820fc7ad126435a1",
"aarch64-unknown-linux-musl-0.11.24":
"222fcd9878ed6f5a8565d121d1a10e1f958e3c7b42e4ec5cbc6927b2be20cc1a",
"arm-unknown-linux-musleabihf-0.11.24":
"92fa5e688c5dac8cf459421ad65f6d21ea1c530dd2225981bcebfd5d538aa4bc",
"armv7-unknown-linux-gnueabihf-0.11.24":
"b9ec27086af022b2962428beeb5d391903d663f3bd76fed3d8a39def1cc7fe19",
"armv7-unknown-linux-musleabihf-0.11.24":
"747560329d0765623f261969fffb8acd35403c280d1baec36bed46d335731aca",
"i686-pc-windows-msvc-0.11.24":
"1df231a0da08d691c32047cce8a4aa0aba82d2a74f02999f2e4847a5e8e7e2df",
"i686-unknown-linux-gnu-0.11.24":
"e7444c509c01efb293d2e0c78d306ec2624aa9d1d63c7fed095e0a3e29800bb9",
"i686-unknown-linux-musl-0.11.24":
"ec692d59d86071fd07d2e7cf8cef8473941019238dd00c0481e6db59b79b4665",
"powerpc64le-unknown-linux-gnu-0.11.24":
"29db3e6f7422f027d14f5718fe3efa4b6b475ada234d7b10b6002ba1367cdc08",
"riscv64gc-unknown-linux-gnu-0.11.24":
"3e975a156d5972359c6b12ce3235a5cf68f838a0be3605c5800996556b902674",
"riscv64gc-unknown-linux-musl-0.11.24":
"4d39d5eec286daab074b5b2ed142cd9d9042306a2996f708f5a86dcd19a35eaf",
"s390x-unknown-linux-gnu-0.11.24":
"a0baa1dc34efcb896c8e82e0844b10e308cf48fb1f5060cef6d12ea5a801c3f7",
"x86_64-apple-darwin-0.11.24":
"8e026ec796a2760c33c832298b0910bf07fb369d00cc075761c321923ac37522",
"x86_64-pc-windows-msvc-0.11.24":
"af9573a2e36f7020b18ec5fdde20117aae74bbad3f4acb3dc3fc03319f1aa083",
"x86_64-unknown-linux-gnu-0.11.24":
"5ce1ad074a78f96c5c8122088bb85a12eb282195bc1453151a48762e4fc31fed",
"x86_64-unknown-linux-musl-0.11.24":
"2f9eaa976cf8ad8574623dde6916fb87ec047524fe65b99dc11e44e33eae4bfb",
"aarch64-apple-darwin-0.11.23":
"71ef9de85db820749b3b12b7585624ee279e9c5afcbc6f8236bc3d628c4305b0",
"aarch64-pc-windows-msvc-0.11.23":
"c24ef88c44c5e6be3daa7357c0cd8419a88de74c5d01f4ff403e06b765249788",
"aarch64-unknown-linux-gnu-0.11.23":
"1873a77350f6621279ae1a0d2227f2bd8b67131598f14a7eb0ba2215d3da2c98",
"aarch64-unknown-linux-musl-0.11.23":
"80efb615b78c1e5721e5858135cd3499609b26741220332c843bd58936053bc6",
"arm-unknown-linux-musleabihf-0.11.23":
"2467703669be1cf02f31f4347a11c9b10a763e794424eb1073b9fc2565ba45fc",
"armv7-unknown-linux-gnueabihf-0.11.23":
"d10df2ebaa729a51d15395720c3f5e76497ae6414beb82043bb2e53f9a86314a",
"armv7-unknown-linux-musleabihf-0.11.23":
"417e901f5873a62af344e25e08fb96d75be3a02628e59da4529b73c3693bd31b",
"i686-pc-windows-msvc-0.11.23":
"6756a552713bd1b9bd1e8d15402db21600c4fbdeae94c70721e0387b6bbad410",
"i686-unknown-linux-gnu-0.11.23":
"87c661f2ccb116dd306b69423fa51f9d7d921ba9bcb1eb8e000853dfbc4b069d",
"i686-unknown-linux-musl-0.11.23":
"dac33b2ca44fc162f074bb3b08ef9018e70df930ea82e4c7906e48704b0cfd12",
"powerpc64le-unknown-linux-gnu-0.11.23":
"1c87278219389da56f49670ac3f9ae72cda4c38c6a1305290961586ce1b98c3b",
"riscv64gc-unknown-linux-gnu-0.11.23":
"38c3b11a79dd6729ff2f35d1882a40c8919e47f385ed51703bed8e1c1ab9afea",
"riscv64gc-unknown-linux-musl-0.11.23":
"2bf05826fe1b29adc8031132581400e1ccca0c0051fcbbfe93bddefb80f8dbeb",
"s390x-unknown-linux-gnu-0.11.23":
"1a13fd6ab96af55456e6f4ca3f173475e0edf9c9f887bf6e0bc545bc2d931fad",
"x86_64-apple-darwin-0.11.23":
"7a88155033cc469bba5bd5a24212e355eb92e3e2a276320b669ec576296c1e25",
"x86_64-pc-windows-msvc-0.11.23":
"02ad29f07e674d68726ba3bb1ff25b335d83515756e2b1a194bb56c3cc30e07c",
"x86_64-unknown-linux-gnu-0.11.23":
"e12c4cda2fe8c305510a78380a88f2c32a27e90cdcd123cefd2873388f0ebb5f",
"x86_64-unknown-linux-musl-0.11.23":
"6be47081100ff1ce0ac7e85ba2ac12e32f2ffa6f946d78bf7f24ee9ce3a46181",
"aarch64-apple-darwin-0.11.22":
"97a45e2ff8d5ea262623eed57ec2d9c468a42d74496d5c3c3eef11340235bd7f",
"aarch64-pc-windows-msvc-0.11.22":
"30fa01e0fc7c78bdaf6f369ebac401f22f0f865d650f0732a26f1df3e2c6971e",
"aarch64-unknown-linux-gnu-0.11.22":
"54b22e9f5570f3643cdf85a33bbc8e9feb3fc6e836a7c660c05378434ef44fe2",
"aarch64-unknown-linux-musl-0.11.22":
"bc28063fe57e76a2d632d804cae9c602f8aded1d99f41025d7fefeb66b10befd",
"arm-unknown-linux-musleabihf-0.11.22":
"950284c836a2f2aff79d64e703b68ea6bc6ee3a5c5f49399a7889cdd5069d78a",
"armv7-unknown-linux-gnueabihf-0.11.22":
"7ff74f488d401f66a0314f65b6970edb011885e09f154e31a307e6d620daad0d",
"armv7-unknown-linux-musleabihf-0.11.22":
"10d3f2a47d8c8c99d6aaafb1ec9c98aaf0b2a8ece43af01141693596b8a5cf35",
"i686-pc-windows-msvc-0.11.22":
"08388dc9258b211236136327c775f74c127a2321c4f66d7dfcd4ae5cc76748fd",
"i686-unknown-linux-gnu-0.11.22":
"522c0d3e0799914cb821661b947d73cd0ee44f6f8ecdadd60bdf11bbf0efef5a",
"i686-unknown-linux-musl-0.11.22":
"47e78c168133cb3dd860090de5e832292e9f6d4e3110edd5028f90500a9d791d",
"powerpc64le-unknown-linux-gnu-0.11.22":
"519602d26c50891254e19eb269c35da44a330e0499107bceb7926b7b061ed455",
"riscv64gc-unknown-linux-gnu-0.11.22":
"291a5506ff058ba68357298752b83b5b230212b0a15845b568029d00cc1d4e8b",
"riscv64gc-unknown-linux-musl-0.11.22":
"dd03fa6c9e6ba49195ba78c401bd838496d77d25c17594b7ff0e8e151c13edb9",
"s390x-unknown-linux-gnu-0.11.22":
"d5149a1bdd21dc8a86ad91d8657a4cd4eb0761a433ef0ce76117720e41ec950e",
"x86_64-apple-darwin-0.11.22":
"9490033dc405b4afc8fa5f9ecd5cc1793e80c727a8c42c32ad456778720d39f2",
"x86_64-pc-windows-msvc-0.11.22":
"b56939bac92d29996d351647f7c6f15b31cc69cf952d06d136de3e1e62eb64d1",
"x86_64-unknown-linux-gnu-0.11.22":
"30075b14624a62021198319f22e08f1651a97d4026a8c84dab6abcbfaba0d81c",
"x86_64-unknown-linux-musl-0.11.22":
"b80bd6952ee11b920b5ab2a35dbccddc364f579e5f2e028d115848cdaa72963e",
"aarch64-apple-darwin-0.11.21":
"1f921d491ba5ffeea774eb04d6681ecee379101341cbb1500394993b541bf3f4",
"aarch64-pc-windows-msvc-0.11.21":
"74e443f8004022dde57a1bd0d10c097830f9ea8feb4ec927db52cd5d805c2f48",
"aarch64-unknown-linux-gnu-0.11.21":
"88e800834007cc5efd4675f166eb2a51e7e3ad19876d85fa8805a6fb5c922397",
"aarch64-unknown-linux-musl-0.11.21":
"e71badaed2a2c3a404a0a00974b51c7ed5f5bc7be947916846005b739c68a5a2",
"arm-unknown-linux-musleabihf-0.11.21":
"7cd6637deebacfa0224e53afb4dd7da4f464ba0ecc128f6f543897c157e39a0f",
"armv7-unknown-linux-gnueabihf-0.11.21":
"929440f991ccd8097e01be1ec831f673ac7bbf508e94819b4270f2873f69e658",
"armv7-unknown-linux-musleabihf-0.11.21":
"20f4b653a17adb09cdfa7f911d46a1f254b918a2b49bef1266c735ab4c6fced0",
"i686-pc-windows-msvc-0.11.21":
"77d7979222c6bd621bdb862c9cb138be41dce1e3cea239b1e87eb82dfac2dbd5",
"i686-unknown-linux-gnu-0.11.21":
"07125219898b1c8e71bc612d91b190927c6b192a7bce5dd029b1c9070e9b7049",
"i686-unknown-linux-musl-0.11.21":
"865eff26cef62b7862854e176d57d9e0164daeec595723132a81aa3611238798",
"powerpc64le-unknown-linux-gnu-0.11.21":
"0e97021d831f9670c8261f9270ecf94b83f1a90ff5312389e37a77676deaec87",
"riscv64gc-unknown-linux-gnu-0.11.21":
"63013d7afe8fd552b273a7a5ca1f1425c0c82b12d73454d24237876bc26006e9",
"riscv64gc-unknown-linux-musl-0.11.21":
"b869fe80435715b2b414443af28de96ed5d7f8c6759e12ba141abca221ebc0cd",
"s390x-unknown-linux-gnu-0.11.21":
"743694a86a05eaf15d292c3d757388c4b2a11b4a7eb67f000077b4d6c467347e",
"x86_64-apple-darwin-0.11.21":
"f3c8e5708a84b920c18b691214d54d2b0da6b984789caae95d47c95120cb7765",
"x86_64-pc-windows-msvc-0.11.21":
"ace861f360c6de2babedc1607d0f454b6b09a820dbc8182dc15af927e4df9589",
"x86_64-unknown-linux-gnu-0.11.21":
"8c88519b0ef0af9801fcdee419bbb12116bd9e6b18e162ae093c932d8b264050",
"x86_64-unknown-linux-musl-0.11.21":
"9dadff5b9e7b1d2d011e41852a1cbca713d9d5d88194f2eb6bd240fa4fb0a719",
"aarch64-apple-darwin-0.11.20":
"0a2b6a757d5693671a7ce0002554ae869604e1e69acb10313ac14d08374be01a",
"aarch64-pc-windows-msvc-0.11.20":
"5248e1a5f980a77f153c4c452511ec6376abb8772b0979c450775f3a268fc61e",
"aarch64-unknown-linux-gnu-0.11.20":
"c8b5b7f9c804b640da0bb66cddddf0a00ce971f64d8076622d70bd141bc80857",
"aarch64-unknown-linux-musl-0.11.20":
"79931fc8e82dddff6eb9cab32aa8492e35bc22767322dd68cad36012411ae6d5",
"arm-unknown-linux-musleabihf-0.11.20":
"4113bf774c0626d82374fcb4de1dbe117f38e68cfd4cf7360e44259ca62a6d48",
"armv7-unknown-linux-gnueabihf-0.11.20":
"b8004b8be37d13ae8c05e8d55028d9ea3ea387fa61a96eeda7812e1a413fd9f8",
"armv7-unknown-linux-musleabihf-0.11.20":
"1a6ab0ebac84282838ec96dadb245b1d4404ea8a40e375822cc5a2f1f3997d72",
"i686-pc-windows-msvc-0.11.20":
"00c3f694414bf3821922dc9766cec373bcceb16ae3d98ff1fd0774b97bbc55fb",
"i686-unknown-linux-gnu-0.11.20":
"08d3e199788ca3ce331da13cc4df15449bd44844a1262597bacea15bb1e42a0d",
"i686-unknown-linux-musl-0.11.20":
"0556b545dcba5402c6120575e66e046f3165df4be6baed6992ba7ebc000054e6",
"powerpc64le-unknown-linux-gnu-0.11.20":
"555c15dfb8e95e9edb5d4c4f442f9a77fe7f007804da164450cac1eddb41bcde",
"riscv64gc-unknown-linux-gnu-0.11.20":
"f78a42002f4c7702688493f7513a081939ca3d52b41dcc7b9224bb660f0c4c28",
"riscv64gc-unknown-linux-musl-0.11.20":
"7433aed57cf15cf0657d0ce7c655f95eb00e54e49a0dc408d0170467260843d9",
"s390x-unknown-linux-gnu-0.11.20":
"208af0d9d0099f86ed093b68b05703451b884f6db351f9555fda905d3538e53d",
"x86_64-apple-darwin-0.11.20":
"bef01a86faab997f6022b45cfa29bfc5b090f2f72cd4a91d2ecefe641efdabe7",
"x86_64-pc-windows-msvc-0.11.20":
"4c281f1b6b9ccb14d9567ebde9a218a7cabfb35e5ffe8c1d4ef5e2821a655474",
"x86_64-unknown-linux-gnu-0.11.20":
"5de211d9278af365497d387e25316907b3b4a9f25b4476dd6dbf238d6f85cff3",
"x86_64-unknown-linux-musl-0.11.20":
"a7545a1360d29baa696753af2f41849047b332bcb90e8ea75ad0568e7f865e35",
"aarch64-apple-darwin-0.11.19":
"d8f59c38e8c4168ee468d423cd63184be12fa6995a4283d41ee1a14d003c9453",
"aarch64-pc-windows-msvc-0.11.19":
"5592a990a9d9901fd0d23992d872f2ec3ca91b7bbd3d5f0bb5e6f42b851493d8",
"aarch64-unknown-linux-gnu-0.11.19":
"83b13ab184a45b7d9a3b0e4b10eaebd50ad41e66cb16dcce8e60aa7be13ae399",
"aarch64-unknown-linux-musl-0.11.19":
"767629b64cdf078c32e42819db28d5ca868b8dc7e3a879967fadc3e4f7f66be3",
"arm-unknown-linux-musleabihf-0.11.19":
"638bb5aac6419e062149b4600c0030e2203dfbb8bf6b0db740755bb19f458abf",
"armv7-unknown-linux-gnueabihf-0.11.19":
"56c307d18acadb1bee2b76bde39cedcf0d29f93102c52f39f8c0c8a42dffe96e",
"armv7-unknown-linux-musleabihf-0.11.19":
"d807c33e89c27430a68b7be52a8a0d39e08c91dba0fa295172c6ff2ce2d07a27",
"i686-pc-windows-msvc-0.11.19":
"e701a69f0ab192fde8ea3f4cfdedfa6cade0c92cea2e350613b6528e966b50c7",
"i686-unknown-linux-gnu-0.11.19":
"d0ef2e69c44a2763161d5b5f40b722fe81e0d16bed217cbc526fb5a3640802b3",
"i686-unknown-linux-musl-0.11.19":
"f58e41864cb91fbd2306a7b46718fdd0c1029169fe03347719771a1c68a527e6",
"powerpc64le-unknown-linux-gnu-0.11.19":
"b6dcd94b5c456d11787d1e6d01b5f9a34b456e22e2dde9bbae3531dc7676c0d8",
"riscv64gc-unknown-linux-gnu-0.11.19":
"4b9ca6bd2ed1e4c2658e5f85cf8a208c192490d9d24dbf2c36d61ea10a389778",
"riscv64gc-unknown-linux-musl-0.11.19":
"184232b4e0de4d6a674d57b1d6d8c39a3c5a5dbc27f95ed972c46974d21b0115",
"s390x-unknown-linux-gnu-0.11.19":
"c4535a5f9f27a94df384ea8140b78c3e7c083d4130cd341fd826b5ab343979fb",
"x86_64-apple-darwin-0.11.19":
"1585f415cade9f061e7f00fe5b00030a79ccfac60c650242ce639ba946138d40",
"x86_64-pc-windows-msvc-0.11.19":
"1665fc8e37b5d70a134820d6d7891747471a2ac8bc940ee7af0b69fd03b28d61",
"x86_64-unknown-linux-gnu-0.11.19":
"7035608168e106375b36d0c818d537a889c51a8625fe7f8f7cad5e62b947c368",
"x86_64-unknown-linux-musl-0.11.19":
"c4c0d0a383413261af5f0f0743e1292f4aafbe907987ed83bd0ac66f0a3d7e20",
"aarch64-apple-darwin-0.11.18":
"1a7adf8dadae3b55853115d13a8bf564d219597ad13824b93b213706933863e5",
"aarch64-pc-windows-msvc-0.11.18":
"0689e1a40d36b387522d2b1b865cd98a15ddd4a7507e256ad93be6f6a335fec1",
"aarch64-unknown-linux-gnu-0.11.18":
"0f03c6648df1c159557f4222c0f37250f84733fb88d6fc3c16770e17c177a8c9",
"aarch64-unknown-linux-musl-0.11.18":
"6d895725333680bf7633ad635baff8e49dc45d3b52e00b2b3adf6ced41f2ebe2",
"arm-unknown-linux-musleabihf-0.11.18":
"c4fe354b28c489fa6649531808076c43eb3a34122df49b0a3005bb75dbf101c3",
"armv7-unknown-linux-gnueabihf-0.11.18":
"a70a8b1124dc1fabcce9f2bbe6591c72a05d49df74125d1c327b5745f2becbb6",
"armv7-unknown-linux-musleabihf-0.11.18":
"f8b6f4df3ff9d142a25892be575ade438672a8353ad71997f7db88e9b9a1062d",
"i686-pc-windows-msvc-0.11.18":
"7505112a7bf72f50391c50f2aa07950b95b3c43c7d9fd4da5626876407d15dda",
"i686-unknown-linux-gnu-0.11.18":
"5f3df0d62af1d174a06b82a6faf1a5e9a1f729b87d11c7d9cd87d4241e04f23d",
"i686-unknown-linux-musl-0.11.18":
"4237cfcd03fb8767a7ec713ab3db14381d83bbd0bf5ccc88cd6f28ac8c2c616f",
"powerpc64le-unknown-linux-gnu-0.11.18":
"fc8f46a198e540ca2d89fd9480da0648d673ff3e25b4048c82ca5c292a478052",
"riscv64gc-unknown-linux-gnu-0.11.18":
"cdb0555db7828bbd1dc24e55171b8ac3dbbc24fe17b6a7387783cd4d543a1538",
"riscv64gc-unknown-linux-musl-0.11.18":
"3d5b533080bb593c82b281b8d289e29d51b97c0994655099845752e948181fe2",
"s390x-unknown-linux-gnu-0.11.18":
"7a91aa963680f2fe14ebf89291cf8eafcff634eccdeb6d301e0252b282171818",
"x86_64-apple-darwin-0.11.18":
"00a61e3db99b53c927a7e6c4ccdccb898aa3253d07928822211e9dc570a25661",
"x86_64-pc-windows-msvc-0.11.18":
"bf8e0021336b7c77bd80a078b612125f385b08f541437edaea8c8ca9e574db0d",
"x86_64-unknown-linux-gnu-0.11.18":
"588f3e360f69ce02b6982aa99f2240e803933a6b7e176ac01617830adf955add",
"x86_64-unknown-linux-musl-0.11.18":
"a095a969fc8357f42e35652e0554525a47a29010ddb814bd82650c2ffa7d6d62",
"aarch64-apple-darwin-0.11.17": "aarch64-apple-darwin-0.11.17":
"2a162f6b90ff3691a2f9cae1622e066a3ce592e110f66670cdcc841324b28226", "2a162f6b90ff3691a2f9cae1622e066a3ce592e110f66670cdcc841324b28226",
"aarch64-pc-windows-msvc-0.11.17": "aarch64-pc-windows-msvc-0.11.17":
+4 -8
View File
@@ -8,7 +8,6 @@ import {
TOOL_CACHE_NAME, TOOL_CACHE_NAME,
VERSIONS_MANIFEST_URL, VERSIONS_MANIFEST_URL,
} from "../utils/constants"; } from "../utils/constants";
import * as log from "../utils/logging";
import type { Architecture, Platform } from "../utils/platforms"; import type { Architecture, Platform } from "../utils/platforms";
import { validateChecksum } from "./checksum/checksum"; import { validateChecksum } from "./checksum/checksum";
import { getArtifact } from "./manifest"; import { getArtifact } from "./manifest";
@@ -37,7 +36,6 @@ export async function downloadVersion(
checksum: string | undefined, checksum: string | undefined,
githubToken: string, githubToken: string,
manifestUrl?: string, manifestUrl?: string,
downloadFromAstralMirror = true,
): Promise<{ version: string; cachedToolDir: string }> { ): Promise<{ version: string; cachedToolDir: string }> {
const artifact = await getArtifact(version, arch, platform, manifestUrl); const artifact = await getArtifact(version, arch, platform, manifestUrl);
@@ -54,9 +52,7 @@ export async function downloadVersion(
? checksum ? checksum
: resolveChecksum(checksum, artifact.checksum); : resolveChecksum(checksum, artifact.checksum);
const mirrorUrl = downloadFromAstralMirror const mirrorUrl = rewriteToMirror(artifact.downloadUrl);
? rewriteToMirror(artifact.downloadUrl)
: undefined;
const downloadUrl = mirrorUrl ?? artifact.downloadUrl; const downloadUrl = mirrorUrl ?? artifact.downloadUrl;
try { try {
@@ -74,7 +70,7 @@ export async function downloadVersion(
throw err; throw err;
} }
log.warning( core.warning(
`Failed to download from mirror, falling back to GitHub Releases: ${(err as Error).message}`, `Failed to download from mirror, falling back to GitHub Releases: ${(err as Error).message}`,
); );
@@ -124,7 +120,7 @@ async function downloadArtifact(
checksum: string | undefined, checksum: string | undefined,
githubToken: string | undefined, githubToken: string | undefined,
): Promise<{ version: string; cachedToolDir: string }> { ): Promise<{ version: string; cachedToolDir: string }> {
log.info(`Downloading uv from "${downloadUrl}" ...`); core.info(`Downloading uv from "${downloadUrl}" ...`);
const downloadPath = await tc.downloadTool( const downloadPath = await tc.downloadTool(
downloadUrl, downloadUrl,
undefined, undefined,
@@ -140,7 +136,7 @@ async function downloadArtifact(
// so this may fail if another tar, like gnu tar, ends up being used. // so this may fail if another tar, like gnu tar, ends up being used.
uvDir = await tc.extractTar(downloadPath, undefined, "x"); uvDir = await tc.extractTar(downloadPath, undefined, "x");
} catch (err) { } catch (err) {
log.info( core.info(
`Extracting with tar failed, falling back to zip extraction: ${(err as Error).message}`, `Extracting with tar failed, falling back to zip extraction: ${(err as Error).message}`,
); );
const extension = getExtension(platform); const extension = getExtension(platform);
+40 -149
View File
@@ -1,9 +1,6 @@
import { createInterface } from "node:readline";
import { Readable } from "node:stream";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { VERSIONS_MANIFEST_URL } from "../utils/constants"; import { VERSIONS_MANIFEST_URL } from "../utils/constants";
import { fetch } from "../utils/fetch"; import { fetch } from "../utils/fetch";
import * as log from "../utils/logging";
import { selectDefaultVariant } from "./variant-selection"; import { selectDefaultVariant } from "./variant-selection";
export interface ManifestArtifact { export interface ManifestArtifact {
@@ -25,26 +22,28 @@ export interface ArtifactResult {
downloadUrl: string; downloadUrl: string;
} }
interface CachedManifest { const cachedManifestData = new Map<string, ManifestVersion[]>();
complete: boolean;
versions: ManifestVersion[];
}
const cachedManifestData = new Map<string, CachedManifest>();
export async function fetchManifest( export async function fetchManifest(
manifestUrl: string = VERSIONS_MANIFEST_URL, manifestUrl: string = VERSIONS_MANIFEST_URL,
): Promise<ManifestVersion[]> { ): Promise<ManifestVersion[]> {
const cachedManifest = cachedManifestData.get(manifestUrl); const cachedVersions = cachedManifestData.get(manifestUrl);
if (cachedManifest?.complete === true) { if (cachedVersions !== undefined) {
core.debug(`Using cached manifest data from ${manifestUrl}`); core.debug(`Using cached manifest data from ${manifestUrl}`);
return cachedManifest.versions; return cachedVersions;
}
core.info(`Fetching manifest data from ${manifestUrl} ...`);
const response = await fetch(manifestUrl, {});
if (!response.ok) {
throw new Error(
`Failed to fetch manifest data: ${response.status} ${response.statusText}`,
);
} }
const response = await fetchManifestResponse(manifestUrl);
const body = await response.text(); const body = await response.text();
const versions = parseManifest(body, manifestUrl); const versions = parseManifest(body, manifestUrl);
cachedManifestData.set(manifestUrl, { complete: true, versions }); cachedManifestData.set(manifestUrl, versions);
return versions; return versions;
} }
@@ -57,7 +56,11 @@ export function parseManifest(
throw new Error(`Manifest at ${sourceDescription} is empty.`); throw new Error(`Manifest at ${sourceDescription} is empty.`);
} }
rejectLegacyManifest(trimmed, sourceDescription); if (trimmed.startsWith("[")) {
throw new Error(
`Legacy JSON array manifests are no longer supported in ${sourceDescription}. Use the astral-sh/versions manifest format instead.`,
);
}
const versions: ManifestVersion[] = []; const versions: ManifestVersion[] = [];
@@ -67,7 +70,22 @@ export function parseManifest(
continue; continue;
} }
versions.push(parseManifestRecord(record, sourceDescription, index + 1)); let parsed: unknown;
try {
parsed = JSON.parse(record);
} catch (error) {
throw new Error(
`Failed to parse manifest data from ${sourceDescription} at line ${index + 1}: ${(error as Error).message}`,
);
}
if (!isManifestVersion(parsed)) {
throw new Error(
`Invalid manifest record in ${sourceDescription} at line ${index + 1}.`,
);
}
versions.push(parsed);
} }
if (versions.length === 0) { if (versions.length === 0) {
@@ -80,10 +98,7 @@ export function parseManifest(
export async function getLatestVersion( export async function getLatestVersion(
manifestUrl: string = VERSIONS_MANIFEST_URL, manifestUrl: string = VERSIONS_MANIFEST_URL,
): Promise<string> { ): Promise<string> {
const latestVersion = const latestVersion = (await fetchManifest(manifestUrl))[0]?.version;
manifestUrl === VERSIONS_MANIFEST_URL
? (await findManifestVersion(() => true))?.version
: (await fetchManifest(manifestUrl))[0]?.version;
if (latestVersion === undefined) { if (latestVersion === undefined) {
throw new Error("No versions found in manifest data"); throw new Error("No versions found in manifest data");
@@ -93,20 +108,10 @@ export async function getLatestVersion(
return latestVersion; return latestVersion;
} }
// The default manifest is guaranteed to be ordered newest-first:
// https://github.com/astral-sh/versions#format
export async function getFirstMatchingVersion(
predicate: (version: string) => boolean,
): Promise<string | undefined> {
return (
await findManifestVersion((versionData) => predicate(versionData.version))
)?.version;
}
export async function getAllVersions( export async function getAllVersions(
manifestUrl: string = VERSIONS_MANIFEST_URL, manifestUrl: string = VERSIONS_MANIFEST_URL,
): Promise<string[]> { ): Promise<string[]> {
log.info( core.info(
`Getting available versions from ${manifestSource(manifestUrl)} ...`, `Getting available versions from ${manifestSource(manifestUrl)} ...`,
); );
const versions = await fetchManifest(manifestUrl); const versions = await fetchManifest(manifestUrl);
@@ -119,12 +124,10 @@ export async function getArtifact(
platform: string, platform: string,
manifestUrl: string = VERSIONS_MANIFEST_URL, manifestUrl: string = VERSIONS_MANIFEST_URL,
): Promise<ArtifactResult | undefined> { ): Promise<ArtifactResult | undefined> {
const versionData = const versions = await fetchManifest(manifestUrl);
manifestUrl === VERSIONS_MANIFEST_URL const versionData = versions.find(
? await findManifestVersion((candidate) => candidate.version === version) (candidate) => candidate.version === version,
: (await fetchManifest(manifestUrl)).find( );
(candidate) => candidate.version === version,
);
if (!versionData) { if (!versionData) {
core.debug(`Version ${version} not found in manifest ${manifestUrl}`); core.debug(`Version ${version} not found in manifest ${manifestUrl}`);
return undefined; return undefined;
@@ -165,87 +168,6 @@ export function clearManifestCache(manifestUrl?: string): void {
cachedManifestData.delete(manifestUrl); cachedManifestData.delete(manifestUrl);
} }
async function fetchManifestResponse(manifestUrl: string) {
log.info(`Fetching manifest data from ${manifestUrl} ...`);
const response = await fetch(manifestUrl, {});
if (!response.ok) {
throw new Error(
`Failed to fetch manifest data: ${response.status} ${response.statusText}`,
);
}
return response;
}
async function findManifestVersion(
predicate: (versionData: ManifestVersion) => boolean,
): Promise<ManifestVersion | undefined> {
const cachedManifest = cachedManifestData.get(VERSIONS_MANIFEST_URL);
const cachedVersion = cachedManifest?.versions.find(predicate);
if (cachedVersion !== undefined || cachedManifest?.complete === true) {
return cachedVersion;
}
const response = await fetchManifestResponse(VERSIONS_MANIFEST_URL);
if (response.body === null) {
const versions = parseManifest(
await response.text(),
VERSIONS_MANIFEST_URL,
);
cachedManifestData.set(VERSIONS_MANIFEST_URL, {
complete: true,
versions,
});
return versions.find(predicate);
}
const input = Readable.fromWeb(response.body);
const lines = createInterface({ crlfDelay: Number.POSITIVE_INFINITY, input });
const versions: ManifestVersion[] = [];
let complete = false;
let lineNumber = 0;
let matchedVersion: ManifestVersion | undefined;
try {
for await (const line of lines) {
lineNumber += 1;
const record = line.trim();
if (record === "") {
continue;
}
if (versions.length === 0) {
rejectLegacyManifest(record, VERSIONS_MANIFEST_URL);
}
const versionData = parseManifestRecord(
record,
VERSIONS_MANIFEST_URL,
lineNumber,
);
versions.push(versionData);
if (predicate(versionData)) {
matchedVersion = versionData;
break;
}
}
complete = matchedVersion === undefined;
} finally {
lines.close();
if (!complete) {
input.destroy();
}
}
if (versions.length === 0) {
throw new Error(`Manifest at ${VERSIONS_MANIFEST_URL} is empty.`);
}
cachedManifestData.set(VERSIONS_MANIFEST_URL, { complete, versions });
return matchedVersion;
}
function manifestSource(manifestUrl: string): string { function manifestSource(manifestUrl: string): string {
if (manifestUrl === VERSIONS_MANIFEST_URL) { if (manifestUrl === VERSIONS_MANIFEST_URL) {
return VERSIONS_MANIFEST_URL; return VERSIONS_MANIFEST_URL;
@@ -254,37 +176,6 @@ function manifestSource(manifestUrl: string): string {
return `manifest-file ${manifestUrl}`; return `manifest-file ${manifestUrl}`;
} }
function parseManifestRecord(
record: string,
sourceDescription: string,
lineNumber: number,
): ManifestVersion {
let parsed: unknown;
try {
parsed = JSON.parse(record);
} catch (error) {
throw new Error(
`Failed to parse manifest data from ${sourceDescription} at line ${lineNumber}: ${(error as Error).message}`,
);
}
if (!isManifestVersion(parsed)) {
throw new Error(
`Invalid manifest record in ${sourceDescription} at line ${lineNumber}.`,
);
}
return parsed;
}
function rejectLegacyManifest(data: string, sourceDescription: string): void {
if (data.startsWith("[")) {
throw new Error(
`Legacy JSON array manifests are no longer supported in ${sourceDescription}. Use the astral-sh/versions manifest format instead.`,
);
}
}
function isManifestVersion(value: unknown): value is ManifestVersion { function isManifestVersion(value: unknown): value is ManifestVersion {
if (!isRecord(value)) { if (!isRecord(value)) {
return false; return false;
+2 -2
View File
@@ -2,8 +2,8 @@ import * as crypto from "node:crypto";
import * as fs from "node:fs"; import * as fs from "node:fs";
import * as stream from "node:stream"; import * as stream from "node:stream";
import * as util from "node:util"; import * as util from "node:util";
import * as core from "@actions/core";
import { create } from "@actions/glob"; import { create } from "@actions/glob";
import * as log from "../utils/logging";
/** /**
* Hashes files matching the given glob pattern. * Hashes files matching the given glob pattern.
@@ -19,7 +19,7 @@ export async function hashFiles(
): Promise<string> { ): Promise<string> {
const globber = await create(pattern); const globber = await create(pattern);
let hasMatch = false; let hasMatch = false;
const writeDelegate = verbose ? log.info : log.debug; const writeDelegate = verbose ? core.info : core.debug;
const result = crypto.createHash("sha256"); const result = crypto.createHash("sha256");
let count = 0; let count = 0;
for await (const file of globber.globGenerator()) { for await (const file of globber.globGenerator()) {
+12 -31
View File
@@ -10,27 +10,6 @@ import {
} from "./cache/restore-cache"; } from "./cache/restore-cache";
import { STATE_UV_PATH, STATE_UV_VERSION } from "./utils/constants"; import { STATE_UV_PATH, STATE_UV_VERSION } from "./utils/constants";
import { loadInputs, type SetupInputs } from "./utils/inputs"; import { loadInputs, type SetupInputs } from "./utils/inputs";
import * as log from "./utils/logging";
function formatUnexpectedFailure(error: unknown): string {
if (error instanceof Error) {
return error.stack ?? error.message;
}
return String(error);
}
function failUnexpectedly(event: string, error: unknown): never {
core.setFailed(`${event}: ${formatUnexpectedFailure(error)}`);
process.exit(1);
}
process.on("uncaughtException", (error) => {
failUnexpectedly("Uncaught exception", error);
});
process.on("unhandledRejection", (reason) => {
failUnexpectedly("Unhandled promise rejection", reason);
});
export async function run(): Promise<void> { export async function run(): Promise<void> {
try { try {
@@ -39,7 +18,7 @@ export async function run(): Promise<void> {
if (inputs.saveCache) { if (inputs.saveCache) {
await saveCache(inputs); await saveCache(inputs);
} else { } else {
log.info("save-cache is false. Skipping save cache step."); core.info("save-cache is false. Skipping save cache step.");
} }
// https://github.com/nodejs/node/issues/56645#issuecomment-3924958861 // https://github.com/nodejs/node/issues/56645#issuecomment-3924958861
await new Promise((resolve) => setTimeout(resolve, 100)); await new Promise((resolve) => setTimeout(resolve, 100));
@@ -62,11 +41,11 @@ async function saveCache(inputs: SetupInputs): Promise<void> {
const matchedKey = core.getState(STATE_CACHE_MATCHED_KEY); const matchedKey = core.getState(STATE_CACHE_MATCHED_KEY);
if (!cacheKey) { if (!cacheKey) {
log.warning("Error retrieving cache key from state."); core.warning("Error retrieving cache key from state.");
return; return;
} }
if (matchedKey === cacheKey) { if (matchedKey === cacheKey) {
log.info(`Cache hit occurred on key ${cacheKey}, not saving cache.`); core.info(`Cache hit occurred on key ${cacheKey}, not saving cache.`);
} else { } else {
if (inputs.pruneCache) { if (inputs.pruneCache) {
await pruneCache(); await pruneCache();
@@ -75,7 +54,7 @@ async function saveCache(inputs: SetupInputs): Promise<void> {
const actualCachePath = getUvCachePath(inputs); const actualCachePath = getUvCachePath(inputs);
if (!fs.existsSync(actualCachePath)) { if (!fs.existsSync(actualCachePath)) {
if (inputs.ignoreNothingToCache) { if (inputs.ignoreNothingToCache) {
log.info( core.info(
"No cacheable uv cache paths were found. Ignoring because ignore-nothing-to-cache is enabled.", "No cacheable uv cache paths were found. Ignoring because ignore-nothing-to-cache is enabled.",
); );
} else { } else {
@@ -95,7 +74,7 @@ async function saveCache(inputs: SetupInputs): Promise<void> {
if (inputs.cachePython) { if (inputs.cachePython) {
if (!fs.existsSync(inputs.pythonDir)) { if (!fs.existsSync(inputs.pythonDir)) {
log.warning( core.warning(
`Python cache path ${inputs.pythonDir} does not exist on disk. Skipping Python cache save because no managed Python installation was found. If you want uv to install managed Python instead of using a system interpreter, set UV_PYTHON_PREFERENCE=only-managed.`, `Python cache path ${inputs.pythonDir} does not exist on disk. Skipping Python cache save because no managed Python installation was found. If you want uv to install managed Python instead of using a system interpreter, set UV_PYTHON_PREFERENCE=only-managed.`,
); );
return; return;
@@ -122,7 +101,7 @@ async function pruneCache(): Promise<void> {
execArgs.push("--force"); execArgs.push("--force");
} }
log.info("Pruning cache..."); core.info("Pruning cache...");
const uvPath = core.getState(STATE_UV_PATH); const uvPath = core.getState(STATE_UV_PATH);
await exec.exec(uvPath, execArgs, options); await exec.exec(uvPath, execArgs, options);
} }
@@ -137,7 +116,7 @@ function getUvCachePath(inputs: SetupInputs): string {
process.env.UV_CACHE_DIR && process.env.UV_CACHE_DIR &&
process.env.UV_CACHE_DIR !== inputs.cacheLocalPath.path process.env.UV_CACHE_DIR !== inputs.cacheLocalPath.path
) { ) {
log.warning( core.warning(
`The environment variable UV_CACHE_DIR has been changed to "${process.env.UV_CACHE_DIR}", by an action or step running after astral-sh/setup-uv. This can lead to unexpected behavior. If you expected this to happen set the cache-local-path input to "${process.env.UV_CACHE_DIR}" instead of "${inputs.cacheLocalPath.path}".`, `The environment variable UV_CACHE_DIR has been changed to "${process.env.UV_CACHE_DIR}", by an action or step running after astral-sh/setup-uv. This can lead to unexpected behavior. If you expected this to happen set the cache-local-path input to "${process.env.UV_CACHE_DIR}" instead of "${inputs.cacheLocalPath.path}".`,
); );
return process.env.UV_CACHE_DIR; return process.env.UV_CACHE_DIR;
@@ -154,13 +133,15 @@ async function saveCacheToKey(
const matchedKey = core.getState(stateKey); const matchedKey = core.getState(stateKey);
if (matchedKey === cacheKey) { if (matchedKey === cacheKey) {
log.info(`${cacheName} hit occurred on key ${cacheKey}, not saving cache.`); core.info(
`${cacheName} hit occurred on key ${cacheKey}, not saving cache.`,
);
return; return;
} }
log.info(`Including ${cacheName} path: ${cachePath}`); core.info(`Including ${cacheName} path: ${cachePath}`);
await cache.saveCache([cachePath], cacheKey); await cache.saveCache([cachePath], cacheKey);
log.info(`${cacheName} saved with key: ${cacheKey}`); core.info(`${cacheName} saved with key: ${cacheKey}`);
} }
run(); run();
+21 -43
View File
@@ -9,7 +9,6 @@ import {
} from "./download/download-version"; } from "./download/download-version";
import { STATE_UV_PATH, STATE_UV_VERSION } from "./utils/constants"; import { STATE_UV_PATH, STATE_UV_VERSION } from "./utils/constants";
import { CacheLocalSource, loadInputs, type SetupInputs } from "./utils/inputs"; import { CacheLocalSource, loadInputs, type SetupInputs } from "./utils/inputs";
import * as log from "./utils/logging";
import { import {
type Architecture, type Architecture,
getArch, getArch,
@@ -20,26 +19,6 @@ import { resolveUvVersion } from "./version/resolve";
const sourceDir = __dirname; const sourceDir = __dirname;
function formatUnexpectedFailure(error: unknown): string {
if (error instanceof Error) {
return error.stack ?? error.message;
}
return String(error);
}
function failUnexpectedly(event: string, error: unknown): never {
core.setFailed(`${event}: ${formatUnexpectedFailure(error)}`);
process.exit(1);
}
process.on("uncaughtException", (error) => {
failUnexpectedly("Uncaught exception", error);
});
process.on("unhandledRejection", (reason) => {
failUnexpectedly("Unhandled promise rejection", reason);
});
async function getPythonVersion(inputs: SetupInputs): Promise<string> { async function getPythonVersion(inputs: SetupInputs): Promise<string> {
if (inputs.pythonVersion !== "") { if (inputs.pythonVersion !== "") {
return inputs.pythonVersion; return inputs.pythonVersion;
@@ -97,7 +76,7 @@ async function run(): Promise<void> {
core.setOutput("uv-version", setupResult.version); core.setOutput("uv-version", setupResult.version);
core.saveState(STATE_UV_VERSION, setupResult.version); core.saveState(STATE_UV_VERSION, setupResult.version);
log.info(`Successfully installed uv version ${setupResult.version}`); core.info(`Successfully installed uv version ${setupResult.version}`);
const detectedPythonVersion = await getPythonVersion(inputs); const detectedPythonVersion = await getPythonVersion(inputs);
core.setOutput("python-version", detectedPythonVersion); core.setOutput("python-version", detectedPythonVersion);
@@ -116,11 +95,11 @@ async function run(): Promise<void> {
function detectEmptyWorkdir(inputs: SetupInputs): void { function detectEmptyWorkdir(inputs: SetupInputs): void {
if (fs.readdirSync(inputs.workingDirectory).length === 0) { if (fs.readdirSync(inputs.workingDirectory).length === 0) {
if (inputs.ignoreEmptyWorkdir) { if (inputs.ignoreEmptyWorkdir) {
log.info( core.info(
"Empty workdir detected. Ignoring because ignore-empty-workdir is enabled", "Empty workdir detected. Ignoring because ignore-empty-workdir is enabled",
); );
} else { } else {
log.warning( core.warning(
"Empty workdir detected. This may cause unexpected behavior. You can enable ignore-empty-workdir to mute this warning.", "Empty workdir detected. This may cause unexpected behavior. You can enable ignore-empty-workdir to mute this warning.",
); );
} }
@@ -141,7 +120,7 @@ async function setupUv(
}); });
const toolCacheResult = tryGetFromToolCache(arch, resolvedVersion); const toolCacheResult = tryGetFromToolCache(arch, resolvedVersion);
if (toolCacheResult.installedPath) { if (toolCacheResult.installedPath) {
log.info(`Found uv in tool-cache for ${toolCacheResult.version}`); core.info(`Found uv in tool-cache for ${toolCacheResult.version}`);
return { return {
uvDir: toolCacheResult.installedPath, uvDir: toolCacheResult.installedPath,
version: toolCacheResult.version, version: toolCacheResult.version,
@@ -155,7 +134,6 @@ async function setupUv(
inputs.checksum, inputs.checksum,
inputs.githubToken, inputs.githubToken,
inputs.manifestFile, inputs.manifestFile,
inputs.downloadFromAstralMirror,
); );
return { return {
@@ -169,39 +147,39 @@ function addUvToPathAndOutput(cachedPath: string): void {
core.saveState(STATE_UV_PATH, `${cachedPath}${path.sep}uv`); core.saveState(STATE_UV_PATH, `${cachedPath}${path.sep}uv`);
core.setOutput("uvx-path", `${cachedPath}${path.sep}uvx`); core.setOutput("uvx-path", `${cachedPath}${path.sep}uvx`);
if (process.env.UV_NO_MODIFY_PATH !== undefined) { if (process.env.UV_NO_MODIFY_PATH !== undefined) {
log.info("UV_NO_MODIFY_PATH is set, not modifying PATH"); core.info("UV_NO_MODIFY_PATH is set, not modifying PATH");
} else { } else {
core.addPath(cachedPath); core.addPath(cachedPath);
log.info(`Added ${cachedPath} to the path`); core.info(`Added ${cachedPath} to the path`);
} }
} }
function addToolBinToPath(inputs: SetupInputs): void { function addToolBinToPath(inputs: SetupInputs): void {
if (inputs.toolBinDir !== undefined) { if (inputs.toolBinDir !== undefined) {
core.exportVariable("UV_TOOL_BIN_DIR", inputs.toolBinDir); core.exportVariable("UV_TOOL_BIN_DIR", inputs.toolBinDir);
log.info(`Set UV_TOOL_BIN_DIR to ${inputs.toolBinDir}`); core.info(`Set UV_TOOL_BIN_DIR to ${inputs.toolBinDir}`);
if (process.env.UV_NO_MODIFY_PATH !== undefined) { if (process.env.UV_NO_MODIFY_PATH !== undefined) {
log.info( core.info(
`UV_NO_MODIFY_PATH is set, not adding ${inputs.toolBinDir} to path`, `UV_NO_MODIFY_PATH is set, not adding ${inputs.toolBinDir} to path`,
); );
} else { } else {
core.addPath(inputs.toolBinDir); core.addPath(inputs.toolBinDir);
log.info(`Added ${inputs.toolBinDir} to the path`); core.info(`Added ${inputs.toolBinDir} to the path`);
} }
} else { } else {
if (process.env.UV_NO_MODIFY_PATH !== undefined) { if (process.env.UV_NO_MODIFY_PATH !== undefined) {
log.info("UV_NO_MODIFY_PATH is set, not adding user local bin to path"); core.info("UV_NO_MODIFY_PATH is set, not adding user local bin to path");
return; return;
} }
if (process.env.XDG_BIN_HOME !== undefined) { if (process.env.XDG_BIN_HOME !== undefined) {
core.addPath(process.env.XDG_BIN_HOME); core.addPath(process.env.XDG_BIN_HOME);
log.info(`Added ${process.env.XDG_BIN_HOME} to the path`); core.info(`Added ${process.env.XDG_BIN_HOME} to the path`);
} else if (process.env.XDG_DATA_HOME !== undefined) { } else if (process.env.XDG_DATA_HOME !== undefined) {
core.addPath(`${process.env.XDG_DATA_HOME}/../bin`); core.addPath(`${process.env.XDG_DATA_HOME}/../bin`);
log.info(`Added ${process.env.XDG_DATA_HOME}/../bin to the path`); core.info(`Added ${process.env.XDG_DATA_HOME}/../bin to the path`);
} else { } else {
core.addPath(`${process.env.HOME}/.local/bin`); core.addPath(`${process.env.HOME}/.local/bin`);
log.info(`Added ${process.env.HOME}/.local/bin to the path`); core.info(`Added ${process.env.HOME}/.local/bin to the path`);
} }
} }
} }
@@ -209,25 +187,25 @@ function addToolBinToPath(inputs: SetupInputs): void {
function setToolDir(inputs: SetupInputs): void { function setToolDir(inputs: SetupInputs): void {
if (inputs.toolDir !== undefined) { if (inputs.toolDir !== undefined) {
core.exportVariable("UV_TOOL_DIR", inputs.toolDir); core.exportVariable("UV_TOOL_DIR", inputs.toolDir);
log.info(`Set UV_TOOL_DIR to ${inputs.toolDir}`); core.info(`Set UV_TOOL_DIR to ${inputs.toolDir}`);
} }
} }
function addPythonDirToPath(inputs: SetupInputs): void { function addPythonDirToPath(inputs: SetupInputs): void {
core.exportVariable("UV_PYTHON_INSTALL_DIR", inputs.pythonDir); core.exportVariable("UV_PYTHON_INSTALL_DIR", inputs.pythonDir);
log.info(`Set UV_PYTHON_INSTALL_DIR to ${inputs.pythonDir}`); core.info(`Set UV_PYTHON_INSTALL_DIR to ${inputs.pythonDir}`);
if (process.env.UV_NO_MODIFY_PATH !== undefined) { if (process.env.UV_NO_MODIFY_PATH !== undefined) {
log.info("UV_NO_MODIFY_PATH is set, not adding python dir to path"); core.info("UV_NO_MODIFY_PATH is set, not adding python dir to path");
} else { } else {
core.addPath(inputs.pythonDir); core.addPath(inputs.pythonDir);
log.info(`Added ${inputs.pythonDir} to the path`); core.info(`Added ${inputs.pythonDir} to the path`);
} }
} }
function setupPython(inputs: SetupInputs): void { function setupPython(inputs: SetupInputs): void {
if (inputs.pythonVersion !== "") { if (inputs.pythonVersion !== "") {
core.exportVariable("UV_PYTHON", inputs.pythonVersion); core.exportVariable("UV_PYTHON", inputs.pythonVersion);
log.info(`Set UV_PYTHON to ${inputs.pythonVersion}`); core.info(`Set UV_PYTHON to ${inputs.pythonVersion}`);
} }
} }
@@ -239,7 +217,7 @@ async function activateEnvironment(inputs: SetupInputs): Promise<void> {
); );
} }
log.info(`Creating and activating python venv at ${inputs.venvPath}...`); core.info(`Creating and activating python venv at ${inputs.venvPath}...`);
const venvArgs = [ const venvArgs = [
"venv", "venv",
inputs.venvPath, inputs.venvPath,
@@ -265,13 +243,13 @@ async function activateEnvironment(inputs: SetupInputs): Promise<void> {
function setCacheDir(inputs: SetupInputs): void { function setCacheDir(inputs: SetupInputs): void {
if (inputs.cacheLocalPath !== undefined) { if (inputs.cacheLocalPath !== undefined) {
if (inputs.cacheLocalPath.source === CacheLocalSource.Config) { if (inputs.cacheLocalPath.source === CacheLocalSource.Config) {
log.info( core.info(
"Using cache-dir from uv config file, not modifying UV_CACHE_DIR", "Using cache-dir from uv config file, not modifying UV_CACHE_DIR",
); );
return; return;
} }
core.exportVariable("UV_CACHE_DIR", inputs.cacheLocalPath.path); core.exportVariable("UV_CACHE_DIR", inputs.cacheLocalPath.path);
log.info(`Set UV_CACHE_DIR to ${inputs.cacheLocalPath.path}`); core.info(`Set UV_CACHE_DIR to ${inputs.cacheLocalPath.path}`);
} }
} }
+1 -2
View File
@@ -10,7 +10,6 @@ import {
getLatestVersion, getLatestVersion,
type ManifestVersion, type ManifestVersion,
} from "./download/manifest"; } from "./download/manifest";
import * as log from "./utils/logging";
const VERSION_IN_CHECKSUM_KEY_PATTERN = const VERSION_IN_CHECKSUM_KEY_PATTERN =
/-(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/; /-(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/;
@@ -27,7 +26,7 @@ async function run(): Promise<void> {
const latestKnownVersion = getLatestKnownVersionFromChecksums(); const latestKnownVersion = getLatestKnownVersionFromChecksums();
if (semver.lte(latestVersion, latestKnownVersion)) { if (semver.lte(latestVersion, latestKnownVersion)) {
log.info( core.info(
`Latest release (${latestVersion}) is not newer than the latest known version (${latestKnownVersion}). Skipping update.`, `Latest release (${latestVersion}) is not newer than the latest known version (${latestKnownVersion}). Skipping update.`,
); );
return; return;
+1 -1
View File
@@ -16,7 +16,7 @@ export function getProxyAgent() {
export const fetch = async (url: string, opts: RequestInit) => { export const fetch = async (url: string, opts: RequestInit) => {
// Merge timeout signal with any existing signal from opts // Merge timeout signal with any existing signal from opts
const timeoutSignal = AbortSignal.timeout(5_000); const timeoutSignal = AbortSignal.timeout(30_000);
const existingSignal = opts.signal; const existingSignal = opts.signal;
const mergedSignal = existingSignal const mergedSignal = existingSignal
? AbortSignal.any([timeoutSignal, existingSignal]) ? AbortSignal.any([timeoutSignal, existingSignal])
+5 -13
View File
@@ -1,7 +1,6 @@
import path from "node:path"; import path from "node:path";
import * as core from "@actions/core"; import * as core from "@actions/core";
import { getConfigValueFromTomlFile } from "./config-file"; import { getConfigValueFromTomlFile } from "./config-file";
import * as log from "./logging";
export enum CacheLocalSource { export enum CacheLocalSource {
Input, Input,
@@ -41,9 +40,7 @@ export interface SetupInputs {
pythonDir: string; pythonDir: string;
githubToken: string; githubToken: string;
manifestFile?: string; manifestFile?: string;
downloadFromAstralMirror: boolean;
addProblemMatchers: boolean; addProblemMatchers: boolean;
quiet: boolean;
resolutionStrategy: ResolutionStrategy; resolutionStrategy: ResolutionStrategy;
} }
@@ -76,10 +73,7 @@ export function loadInputs(): SetupInputs {
const pythonDir = getUvPythonDir(); const pythonDir = getUvPythonDir();
const githubToken = core.getInput("github-token"); const githubToken = core.getInput("github-token");
const manifestFile = getManifestFile(); const manifestFile = getManifestFile();
const downloadFromAstralMirror =
core.getInput("download-from-astral-mirror") === "true";
const addProblemMatchers = core.getInput("add-problem-matchers") === "true"; const addProblemMatchers = core.getInput("add-problem-matchers") === "true";
const quiet = core.getInput("quiet") === "true";
const resolutionStrategy = getResolutionStrategy(); const resolutionStrategy = getResolutionStrategy();
return { return {
@@ -90,7 +84,6 @@ export function loadInputs(): SetupInputs {
cachePython, cachePython,
cacheSuffix, cacheSuffix,
checksum, checksum,
downloadFromAstralMirror,
enableCache, enableCache,
githubToken, githubToken,
ignoreEmptyWorkdir, ignoreEmptyWorkdir,
@@ -100,7 +93,6 @@ export function loadInputs(): SetupInputs {
pruneCache, pruneCache,
pythonDir, pythonDir,
pythonVersion, pythonVersion,
quiet,
resolutionStrategy, resolutionStrategy,
restoreCache, restoreCache,
saveCache, saveCache,
@@ -129,7 +121,7 @@ function getVenvPath(
const venvPathInput = core.getInput("venv-path"); const venvPathInput = core.getInput("venv-path");
if (venvPathInput !== "") { if (venvPathInput !== "") {
if (!activateEnvironment) { if (!activateEnvironment) {
log.warning("venv-path is only used when activate-environment is true"); core.warning("venv-path is only used when activate-environment is true");
} }
const tildeExpanded = expandTilde(venvPathInput); const tildeExpanded = expandTilde(venvPathInput);
return normalizePath(resolveRelativePath(workingDirectory, tildeExpanded)); return normalizePath(resolveRelativePath(workingDirectory, tildeExpanded));
@@ -200,7 +192,7 @@ function getCacheLocalPath(
return { path: cacheDirFromConfig, source: CacheLocalSource.Config }; return { path: cacheDirFromConfig, source: CacheLocalSource.Config };
} }
if (process.env.UV_CACHE_DIR !== undefined) { if (process.env.UV_CACHE_DIR !== undefined) {
log.info(`UV_CACHE_DIR is already set to ${process.env.UV_CACHE_DIR}`); core.info(`UV_CACHE_DIR is already set to ${process.env.UV_CACHE_DIR}`);
return { path: process.env.UV_CACHE_DIR, source: CacheLocalSource.Env }; return { path: process.env.UV_CACHE_DIR, source: CacheLocalSource.Env };
} }
if (enableCache) { if (enableCache) {
@@ -237,12 +229,12 @@ function getCacheDirFromConfig(
try { try {
const cacheDir = getConfigValueFromTomlFile(resolvedPath, "cache-dir"); const cacheDir = getConfigValueFromTomlFile(resolvedPath, "cache-dir");
if (cacheDir !== undefined) { if (cacheDir !== undefined) {
log.info(`Found cache-dir in ${resolvedPath}: ${cacheDir}`); core.info(`Found cache-dir in ${resolvedPath}: ${cacheDir}`);
return cacheDir; return cacheDir;
} }
} catch (err) { } catch (err) {
const message = (err as Error).message; const message = (err as Error).message;
log.warning(`Error while parsing ${filePath}: ${message}`); core.warning(`Error while parsing ${filePath}: ${message}`);
return undefined; return undefined;
} }
} }
@@ -251,7 +243,7 @@ function getCacheDirFromConfig(
export function getUvPythonDir(): string { export function getUvPythonDir(): string {
if (process.env.UV_PYTHON_INSTALL_DIR !== undefined) { if (process.env.UV_PYTHON_INSTALL_DIR !== undefined) {
log.info( core.info(
`UV_PYTHON_INSTALL_DIR is already set to ${process.env.UV_PYTHON_INSTALL_DIR}`, `UV_PYTHON_INSTALL_DIR is already set to ${process.env.UV_PYTHON_INSTALL_DIR}`,
); );
return process.env.UV_PYTHON_INSTALL_DIR; return process.env.UV_PYTHON_INSTALL_DIR;
-21
View File
@@ -1,21 +0,0 @@
import * as core from "@actions/core";
let quiet: boolean | undefined;
function isQuiet(): boolean {
if (quiet === undefined) {
quiet =
typeof core.getInput === "function" && core.getInput("quiet") === "true";
}
return quiet;
}
export function info(msg: string): void {
if (!isQuiet()) {
core.info(msg);
}
}
export const warning = core.warning;
export const error = core.error;
export const debug = core.debug;
+1 -17
View File
@@ -102,7 +102,6 @@ export function getOSNameVersion(): string {
function getLinuxOSNameVersion(): string { function getLinuxOSNameVersion(): string {
const files = ["/etc/os-release", "/usr/lib/os-release"]; const files = ["/etc/os-release", "/usr/lib/os-release"];
let idWithoutVersion: string | undefined;
for (const file of files) { for (const file of files) {
try { try {
@@ -110,9 +109,8 @@ function getLinuxOSNameVersion(): string {
const id = parseOsReleaseValue(content, "ID"); const id = parseOsReleaseValue(content, "ID");
const versionId = parseOsReleaseValue(content, "VERSION_ID"); const versionId = parseOsReleaseValue(content, "VERSION_ID");
// Fallback for rolling releases (debian:unstable/testing, arch, etc.) // Fallback for rolling releases (debian:unstable/testing, arch, etc.)
// that don't have VERSION_ID but have VERSION_CODENAME or BUILD_ID // that don't have VERSION_ID but have VERSION_CODENAME
const versionCodename = parseOsReleaseValue(content, "VERSION_CODENAME"); const versionCodename = parseOsReleaseValue(content, "VERSION_CODENAME");
const buildId = parseOsReleaseValue(content, "BUILD_ID");
if (id && versionId) { if (id && versionId) {
return `${id}-${versionId}`; return `${id}-${versionId}`;
@@ -120,25 +118,11 @@ function getLinuxOSNameVersion(): string {
if (id && versionCodename) { if (id && versionCodename) {
return `${id}-${versionCodename}`; return `${id}-${versionCodename}`;
} }
if (id && buildId) {
return `${id}-${buildId}`;
}
// Remember the ID but keep looking: the next file might still
// provide a version field
if (id && idWithoutVersion === undefined) {
idWithoutVersion = id;
}
} catch { } catch {
// Try next file // Try next file
} }
} }
// Fallback for rolling releases (e.g. void) that have no version
// field at all
if (idWithoutVersion) {
return idWithoutVersion;
}
throw new Error( throw new Error(
"Failed to determine Linux distribution. " + "Failed to determine Linux distribution. " +
"Could not read /etc/os-release or /usr/lib/os-release", "Could not read /etc/os-release or /usr/lib/os-release",
+7 -11
View File
@@ -1,6 +1,6 @@
import fs from "node:fs"; import fs from "node:fs";
import * as core from "@actions/core";
import { getConfigValueFromTomlContent } from "../utils/config-file"; import { getConfigValueFromTomlContent } from "../utils/config-file";
import * as log from "../utils/logging";
import { import {
getUvVersionFromParsedPyproject, getUvVersionFromParsedPyproject,
getUvVersionFromRequirementsText, getUvVersionFromRequirementsText,
@@ -9,7 +9,6 @@ import {
import { normalizeVersionSpecifier } from "./specifier"; import { normalizeVersionSpecifier } from "./specifier";
import { getUvVersionFromToolVersions } from "./tool-versions-file"; import { getUvVersionFromToolVersions } from "./tool-versions-file";
import type { ParsedVersionFile, VersionFileFormat } from "./types"; import type { ParsedVersionFile, VersionFileFormat } from "./types";
import { getUvVersionFromUvLock } from "./uv-lock-file";
interface VersionFileParser { interface VersionFileParser {
format: VersionFileFormat; format: VersionFileFormat;
@@ -50,11 +49,6 @@ const VERSION_FILE_PARSERS: VersionFileParser[] = [
}, },
supports: (filePath) => filePath.endsWith("pyproject.toml"), supports: (filePath) => filePath.endsWith("pyproject.toml"),
}, },
{
format: "uv.lock",
parse: (filePath) => getUvVersionFromUvLock(filePath),
supports: (filePath) => filePath.endsWith("uv.lock"),
},
{ {
format: "requirements", format: "requirements",
parse: (filePath) => { parse: (filePath) => {
@@ -68,10 +62,10 @@ const VERSION_FILE_PARSERS: VersionFileParser[] = [
export function getParsedVersionFile( export function getParsedVersionFile(
filePath: string, filePath: string,
): ParsedVersionFile | undefined { ): ParsedVersionFile | undefined {
log.info(`Trying to find version for uv in: ${filePath}`); core.info(`Trying to find version for uv in: ${filePath}`);
if (!fs.existsSync(filePath)) { if (!fs.existsSync(filePath)) {
log.info(`Could not find file: ${filePath}`); core.info(`Could not find file: ${filePath}`);
return undefined; return undefined;
} }
@@ -87,13 +81,15 @@ export function getParsedVersionFile(
} }
const normalizedSpecifier = normalizeVersionSpecifier(specifier); const normalizedSpecifier = normalizeVersionSpecifier(specifier);
log.info(`Found version for uv in ${filePath}: ${normalizedSpecifier}`); core.info(`Found version for uv in ${filePath}: ${normalizedSpecifier}`);
return { return {
format: parser.format, format: parser.format,
specifier: normalizedSpecifier, specifier: normalizedSpecifier,
}; };
} catch (error) { } catch (error) {
log.warning(`Error while parsing ${filePath}: ${(error as Error).message}`); core.warning(
`Error while parsing ${filePath}: ${(error as Error).message}`,
);
return undefined; return undefined;
} }
} }
+3 -7
View File
@@ -63,11 +63,7 @@ function getUvVersionFromAllDependencies(
allDependencies: string[], allDependencies: string[],
): string | undefined { ): string | undefined {
return allDependencies return allDependencies
.map(getUvVersionFromDependency) .find((dep: string) => dep.match(/^uv[=<>~!]/))
.find((version): version is string => version !== undefined); ?.match(/^uv([=<>~!]+\S*)/)?.[1]
} .trim();
function getUvVersionFromDependency(dependency: string): string | undefined {
const dependencyWithoutMarker = dependency.split(";", 1)[0]?.trim();
return dependencyWithoutMarker?.match(/^uv([=<>~!]+\S*)/)?.[1].trim();
} }
+9 -57
View File
@@ -2,14 +2,8 @@ import * as core from "@actions/core";
import * as tc from "@actions/tool-cache"; import * as tc from "@actions/tool-cache";
import * as pep440 from "@renovatebot/pep440"; import * as pep440 from "@renovatebot/pep440";
import * as semver from "semver"; import * as semver from "semver";
import { import { getAllVersions, getLatestVersion } from "../download/manifest";
getAllVersions,
getFirstMatchingVersion,
getLatestVersion,
} from "../download/manifest";
import { VERSIONS_MANIFEST_URL } from "../utils/constants";
import type { ResolutionStrategy } from "../utils/inputs"; import type { ResolutionStrategy } from "../utils/inputs";
import * as log from "../utils/logging";
import { import {
type ParsedVersionSpecifier, type ParsedVersionSpecifier,
parseVersionSpecifier, parseVersionSpecifier,
@@ -62,7 +56,7 @@ class LatestVersionResolver implements ConcreteVersionResolver {
context.parsedSpecifier.kind === "range" && context.parsedSpecifier.kind === "range" &&
context.parsedSpecifier.isSimpleMinimumVersionSpecifier context.parsedSpecifier.isSimpleMinimumVersionSpecifier
) { ) {
log.info("Found minimum version specifier, using latest version"); core.info("Found minimum version specifier, using latest version");
} }
const latestVersion = await getLatestVersion(context.manifestUrl); const latestVersion = await getLatestVersion(context.manifestUrl);
@@ -87,26 +81,13 @@ class RangeVersionResolver implements ConcreteVersionResolver {
return undefined; return undefined;
} }
let resolvedVersion: string | undefined; const availableVersions = await getAllVersions(context.manifestUrl);
if ( core.debug(`Available versions: ${availableVersions}`);
context.resolutionStrategy === "highest" &&
(context.manifestUrl === undefined || const resolvedVersion =
context.manifestUrl === VERSIONS_MANIFEST_URL) context.resolutionStrategy === "lowest"
) { ? minSatisfying(availableVersions, context.parsedSpecifier.normalized)
resolvedVersion = await findHighestSatisfyingVersion( : maxSatisfying(availableVersions, context.parsedSpecifier.normalized);
context.parsedSpecifier.normalized,
);
} else {
const availableVersions = await getAllVersions(context.manifestUrl);
core.debug(`Available versions: ${availableVersions}`);
resolvedVersion =
context.resolutionStrategy === "lowest"
? minSatisfying(availableVersions, context.parsedSpecifier.normalized)
: maxSatisfying(
availableVersions,
context.parsedSpecifier.normalized,
);
}
if (resolvedVersion === undefined) { if (resolvedVersion === undefined) {
throw new Error(`No version found for ${context.parsedSpecifier.raw}`); throw new Error(`No version found for ${context.parsedSpecifier.raw}`);
@@ -159,35 +140,6 @@ export async function resolveVersion(
throw new Error(`No version found for ${versionInput}`); throw new Error(`No version found for ${versionInput}`);
} }
async function findHighestSatisfyingVersion(
versionSpecifier: string,
): Promise<string | undefined> {
// This fast path assumes uv releases are semver-monotonic: newer manifest
// entries always have higher versions, with no lower-version backports.
const semverRange = semver.validRange(versionSpecifier);
if (semverRange !== null) {
const semverMatch = await getFirstMatchingVersion((version) =>
semver.satisfies(version, semverRange),
);
if (semverMatch !== undefined) {
core.debug(
`Found a version that satisfies the semver range: ${semverMatch}`,
);
return semverMatch;
}
}
const pep440Match = await getFirstMatchingVersion((version) =>
pep440.satisfies(version, versionSpecifier),
);
if (pep440Match !== undefined) {
core.debug(
`Found a version that satisfies the pep440 specifier: ${pep440Match}`,
);
}
return pep440Match;
}
function maxSatisfying( function maxSatisfying(
versions: string[], versions: string[],
version: string, version: string,
-1
View File
@@ -11,7 +11,6 @@ export type VersionFileFormat =
| ".tool-versions" | ".tool-versions"
| "pyproject.toml" | "pyproject.toml"
| "requirements" | "requirements"
| "uv.lock"
| "uv.toml"; | "uv.toml";
export interface ParsedVersionFile { export interface ParsedVersionFile {
-24
View File
@@ -1,24 +0,0 @@
import fs from "node:fs";
import * as toml from "smol-toml";
interface UvLockPackage {
name?: string;
version?: string;
}
interface UvLock {
package?: UvLockPackage[];
}
export function getUvVersionFromUvLock(filePath: string): string | undefined {
const fileContent = fs.readFileSync(filePath, "utf-8");
return getUvVersionFromUvLockContent(fileContent);
}
export function getUvVersionFromUvLockContent(
fileContent: string,
): string | undefined {
const parsed = toml.parse(fileContent) as UvLock;
const uvPackage = parsed.package?.find((pkg) => pkg.name === "uv");
return uvPackage?.version;
}
+2 -2
View File
@@ -1,5 +1,5 @@
import * as path from "node:path"; import * as path from "node:path";
import * as log from "../utils/logging"; import * as core from "@actions/core";
import { getParsedVersionFile } from "./file-parser"; import { getParsedVersionFile } from "./file-parser";
import { normalizeVersionSpecifier } from "./specifier"; import { normalizeVersionSpecifier } from "./specifier";
import type { import type {
@@ -111,7 +111,7 @@ export class WorkspaceVersionResolver implements VersionRequestResolver {
}; };
} }
log.info( core.info(
"Could not determine uv version from uv.toml or pyproject.toml. Falling back to latest.", "Could not determine uv version from uv.toml or pyproject.toml. Falling back to latest.",
); );
return undefined; return undefined;