mirror of
https://github.com/astral-sh/setup-uv.git
synced 2025-01-31 06:22:49 +08:00
Initial commit
This commit is contained in:
commit
18498fc78f
4
.eslintignore
Normal file
4
.eslintignore
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
dist/
|
||||||
|
lib/
|
||||||
|
node_modules/
|
||||||
|
jest.config.js
|
57
.eslintrc.json
Normal file
57
.eslintrc.json
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"plugins": ["jest", "@typescript-eslint"],
|
||||||
|
"extends": ["plugin:github/recommended"],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": 9,
|
||||||
|
"sourceType": "module",
|
||||||
|
"project": "./tsconfig.json"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"no-shadow": "off",
|
||||||
|
"@typescript-eslint/no-shadow": ["error"],
|
||||||
|
"i18n-text/no-en": "off",
|
||||||
|
"eslint-comments/no-use": "off",
|
||||||
|
"import/no-namespace": "off",
|
||||||
|
"no-unused-vars": "off",
|
||||||
|
"@typescript-eslint/no-unused-vars": "error",
|
||||||
|
"@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
|
||||||
|
"@typescript-eslint/no-require-imports": "error",
|
||||||
|
"@typescript-eslint/array-type": "error",
|
||||||
|
"@typescript-eslint/await-thenable": "error",
|
||||||
|
"@typescript-eslint/ban-ts-comment": "error",
|
||||||
|
"camelcase": "off",
|
||||||
|
"@typescript-eslint/consistent-type-assertions": "error",
|
||||||
|
"@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
|
||||||
|
"@typescript-eslint/func-call-spacing": ["error", "never"],
|
||||||
|
"@typescript-eslint/no-array-constructor": "error",
|
||||||
|
"@typescript-eslint/no-empty-interface": "error",
|
||||||
|
"@typescript-eslint/no-explicit-any": "error",
|
||||||
|
"@typescript-eslint/no-extraneous-class": "error",
|
||||||
|
"@typescript-eslint/no-for-in-array": "error",
|
||||||
|
"@typescript-eslint/no-inferrable-types": "error",
|
||||||
|
"@typescript-eslint/no-misused-new": "error",
|
||||||
|
"@typescript-eslint/no-namespace": "error",
|
||||||
|
"@typescript-eslint/no-non-null-assertion": "warn",
|
||||||
|
"@typescript-eslint/no-unnecessary-qualifier": "error",
|
||||||
|
"@typescript-eslint/no-unnecessary-type-assertion": "error",
|
||||||
|
"@typescript-eslint/no-useless-constructor": "error",
|
||||||
|
"@typescript-eslint/no-var-requires": "error",
|
||||||
|
"@typescript-eslint/prefer-for-of": "warn",
|
||||||
|
"@typescript-eslint/prefer-function-type": "warn",
|
||||||
|
"@typescript-eslint/prefer-includes": "error",
|
||||||
|
"@typescript-eslint/prefer-string-starts-ends-with": "error",
|
||||||
|
"@typescript-eslint/promise-function-async": "error",
|
||||||
|
"@typescript-eslint/require-array-sort-compare": "error",
|
||||||
|
"@typescript-eslint/restrict-plus-operands": "error",
|
||||||
|
"semi": "off",
|
||||||
|
"@typescript-eslint/semi": ["error", "never"],
|
||||||
|
"@typescript-eslint/type-annotation-spacing": "error",
|
||||||
|
"@typescript-eslint/unbound-method": "error"
|
||||||
|
},
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es6": true,
|
||||||
|
"jest/globals": true
|
||||||
|
}
|
||||||
|
}
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
* text=auto eol=lf
|
||||||
|
dist/** -diff linguist-generated=true
|
11
.github/dependabot.yml
vendored
Normal file
11
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: github-actions
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: /
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
18
.github/python.json
vendored
Normal file
18
.github/python.json
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"problemMatcher": [
|
||||||
|
{
|
||||||
|
"owner": "python",
|
||||||
|
"pattern": [
|
||||||
|
{
|
||||||
|
"regexp": "^\\s*File\\s\\\"(.*)\\\",\\sline\\s(\\d+),\\sin\\s(.*)$",
|
||||||
|
"file": 1,
|
||||||
|
"line": 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"regexp": "^\\s*raise\\s(.*)\\(\\'(.*)\\'\\)$",
|
||||||
|
"message": 2
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
48
.github/release-drafter.yml
vendored
Normal file
48
.github/release-drafter.yml
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
name-template: 'v$RESOLVED_VERSION 🌈'
|
||||||
|
tag-template: 'v$RESOLVED_VERSION'
|
||||||
|
categories:
|
||||||
|
- title: "🚨 Breaking changes"
|
||||||
|
labels:
|
||||||
|
- "breaking-change"
|
||||||
|
- title: "✨ New features"
|
||||||
|
labels:
|
||||||
|
- "new-feature"
|
||||||
|
- title: "🐛 Bug fixes"
|
||||||
|
labels:
|
||||||
|
- "bugfix"
|
||||||
|
- title: "🚀 Enhancements"
|
||||||
|
labels:
|
||||||
|
- "enhancement"
|
||||||
|
- "refactor"
|
||||||
|
- "performance"
|
||||||
|
- title: "🧰 Maintenance"
|
||||||
|
labels:
|
||||||
|
- "maintenance"
|
||||||
|
- "ci"
|
||||||
|
- title: "📚 Documentation"
|
||||||
|
labels:
|
||||||
|
- "documentation"
|
||||||
|
- title: "⬆️ Dependency updates"
|
||||||
|
labels:
|
||||||
|
- "dependencies"
|
||||||
|
change-template: '- $TITLE @$AUTHOR (#$NUMBER)'
|
||||||
|
change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks.
|
||||||
|
version-resolver:
|
||||||
|
major:
|
||||||
|
labels:
|
||||||
|
- 'major'
|
||||||
|
- 'breaking-change'
|
||||||
|
minor:
|
||||||
|
labels:
|
||||||
|
- 'minor'
|
||||||
|
- 'new-feature'
|
||||||
|
- 'enhancement'
|
||||||
|
patch:
|
||||||
|
labels:
|
||||||
|
- 'patch'
|
||||||
|
- 'bugfix'
|
||||||
|
default: patch
|
||||||
|
template: |
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
$CHANGES
|
53
.github/workflows/check-dist.yml
vendored
Normal file
53
.github/workflows/check-dist.yml
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# `dist/index.js` is a special file in Actions.
|
||||||
|
# When you reference an action with `uses:` in a workflow,
|
||||||
|
# `index.js` is the code that will run.
|
||||||
|
# For our project, we generate this file through a build process from other source files.
|
||||||
|
# We need to make sure the checked-in `index.js` actually matches what we expect it to be.
|
||||||
|
name: Check dist/
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
check-dist:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Set up Node.js 20
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: npm ci
|
||||||
|
|
||||||
|
- name: Rebuild the dist/ directory
|
||||||
|
run: |
|
||||||
|
npm run build
|
||||||
|
npm run package
|
||||||
|
|
||||||
|
- name: Compare the expected and actual dist/ directories
|
||||||
|
run: |
|
||||||
|
if [ "$(git diff --ignore-space-at-eol dist/ | wc -l)" -gt "0" ]; then
|
||||||
|
echo "Detected uncommitted changes after build. See status below:"
|
||||||
|
git diff --text -v
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
id: diff
|
||||||
|
|
||||||
|
# If index.js was different than expected, upload the expected version as an artifact
|
||||||
|
- uses: actions/upload-artifact@v4
|
||||||
|
if: ${{ failure() && steps.diff.conclusion == 'failure' }}
|
||||||
|
with:
|
||||||
|
name: dist
|
||||||
|
path: dist/
|
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
71
.github/workflows/codeql-analysis.yml
vendored
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: [ main ]
|
||||||
|
schedule:
|
||||||
|
- cron: '31 7 * * 3'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'TypeScript' ]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
||||||
|
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v3
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
source-root: src
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 https://git.io/JvXDl
|
||||||
|
|
||||||
|
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||||
|
# and modify them (or add more) to build your code if your project
|
||||||
|
# uses a compiled language
|
||||||
|
|
||||||
|
#- run: |
|
||||||
|
# make bootstrap
|
||||||
|
# make release
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v3
|
19
.github/workflows/release-drafter.yml
vendored
Normal file
19
.github/workflows/release-drafter.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
name: Release Drafter
|
||||||
|
|
||||||
|
# yamllint disable-line rule:truthy
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update_release_draft:
|
||||||
|
name: ✏️ Draft release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: 🚀 Run Release Drafter
|
||||||
|
uses: release-drafter/release-drafter@v6.0.0
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
49
.github/workflows/test-cache-windows.yml
vendored
Normal file
49
.github/workflows/test-cache-windows.yml
vendored
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
name: 'test-cache-windows'
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-setup-cache:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [windows-latest]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup with cache
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
- run: uv sync
|
||||||
|
working-directory: __tests__\fixtures\uv-project
|
||||||
|
test-restore-cache:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [windows-latest]
|
||||||
|
needs: test-setup-cache
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Restore with cache
|
||||||
|
id: restore
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
- name: Cache was hit
|
||||||
|
run: |
|
||||||
|
if ($env:CACHE_HIT -ne "true") {
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
env:
|
||||||
|
CACHE_HIT: ${{ steps.restore.outputs.cache-hit }}
|
||||||
|
- run: uv sync
|
||||||
|
working-directory: __tests__\fixtures\uv-project
|
83
.github/workflows/test-cache.yml
vendored
Normal file
83
.github/workflows/test-cache.yml
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
name: 'test-cache'
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-setup-cache:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, macos-14]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup with cache
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
- run: uv sync
|
||||||
|
working-directory: __tests__/fixtures/uv-project
|
||||||
|
test-restore-cache:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, macos-14]
|
||||||
|
needs: test-setup-cache
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Restore with cache
|
||||||
|
id: restore
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
- name: Cache was hit
|
||||||
|
run: |
|
||||||
|
if [ "$CACHE_HIT" != "true" ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
CACHE_HIT: ${{ steps.restore.outputs.cache-hit }}
|
||||||
|
- run: uv sync
|
||||||
|
working-directory: __tests__/fixtures/uv-project
|
||||||
|
|
||||||
|
test-setup-cache-local:
|
||||||
|
runs-on: oracle-aarch64
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Setup with cache
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
cache-local-path: /tmp/uv-cache
|
||||||
|
- run: uv sync
|
||||||
|
working-directory: __tests__/fixtures/uv-project
|
||||||
|
test-restore-cache-local:
|
||||||
|
runs-on: oracle-aarch64
|
||||||
|
needs: test-setup-cache-local
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Restore with cache
|
||||||
|
id: restore
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
cache-suffix: ${{ github.run_id }}-${{ github.run_attempt }}
|
||||||
|
cache-local-path: /tmp/uv-cache
|
||||||
|
- name: Cache was hit
|
||||||
|
run: |
|
||||||
|
if [ "$CACHE_HIT" != "true" ]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
CACHE_HIT: ${{ steps.restore.outputs.cache-hit }}
|
||||||
|
- run: uv sync
|
||||||
|
working-directory: __tests__/fixtures/uv-project
|
29
.github/workflows/test-latest-windows.yml
vendored
Normal file
29
.github/workflows/test-latest-windows.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
name: 'test-windows'
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test-default-version:
|
||||||
|
runs-on: windows-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Should not be on path
|
||||||
|
run: |
|
||||||
|
if (!(Get-Command -Name "uv" -ErrorAction SilentlyContinue)) {
|
||||||
|
exit 0
|
||||||
|
} else {
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
- name: Setup uv
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- run: uv sync
|
||||||
|
working-directory: __tests__\fixtures\uv-project
|
90
.github/workflows/test.yml
vendored
Normal file
90
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
name: 'test'
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, macos-14, oracle-aarch64]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
- run: |
|
||||||
|
npm install
|
||||||
|
- run: |
|
||||||
|
npm run all
|
||||||
|
test-default-version:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, macos-14, oracle-aarch64]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install default version
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- run: uv sync
|
||||||
|
working-directory: __tests__/fixtures/uv-project
|
||||||
|
test-specific-version:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, macos-latest, macos-14, oracle-aarch64]
|
||||||
|
uv-version: ['latest','0.2.0','0.3.0']
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install version ${{ matrix.uv-version }}
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
version: ${{ matrix.uv-version }}
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- run: uv sync
|
||||||
|
working-directory: __tests__/fixtures/uv-project
|
||||||
|
test-checksum:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [ubuntu-latest, oracle-aarch64]
|
||||||
|
checksum: ['4d9279ad5ca596b1e2d703901d508430eb07564dc4d8837de9e2fca9c90f8ecd']
|
||||||
|
exclude:
|
||||||
|
- os: oracle-aarch64
|
||||||
|
checksum: '4d9279ad5ca596b1e2d703901d508430eb07564dc4d8837de9e2fca9c90f8ecd'
|
||||||
|
include:
|
||||||
|
- os: oracle-aarch64
|
||||||
|
checksum: 'e11b01402ab645392c7ad6044db63d37e4fd1e745e015306993b07695ea5f9f8'
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Checksum matches expected
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
version: '0.3.2'
|
||||||
|
checksum: ${{ matrix.checksum }}
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
test-without-github-token:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install default version
|
||||||
|
uses: ./
|
||||||
|
- run: uv sync
|
||||||
|
working-directory: __tests__/fixtures/uv-project
|
||||||
|
|
||||||
|
test-uvx:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install default version
|
||||||
|
uses: ./
|
||||||
|
- run: uvx ruff --version
|
26
.github/workflows/update-default-version.yml
vendored
Normal file
26
.github/workflows/update-default-version.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
name: 'Update default version and checksums'
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: '20'
|
||||||
|
- name: Update default version and checksums
|
||||||
|
id: update-default-version
|
||||||
|
run: node dist/update-default-version/index.js src/download/checksum/known-checksums.ts action.yml ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- run: npm install && npm run all
|
||||||
|
- name: Create Pull Request
|
||||||
|
uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c # v6.1.0
|
||||||
|
with:
|
||||||
|
commit-message: "chore: update checksums"
|
||||||
|
title: "chore: update default version to ${{ steps.update-default-version.outputs.latest-version }}"
|
||||||
|
body: "chore: update default version to ${{ steps.update-default-version.outputs.latest-version }}"
|
||||||
|
base: main
|
||||||
|
labels: "automated-pr,bugfix"
|
||||||
|
branch: update-default-version-pr
|
||||||
|
delete-branch: true
|
19
.github/workflows/update-major-minor-tags.yml
vendored
Normal file
19
.github/workflows/update-major-minor-tags.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
name: Update Major Minor Tags
|
||||||
|
|
||||||
|
# yamllint disable-line rule:truthy
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches-ignore:
|
||||||
|
- '**'
|
||||||
|
tags:
|
||||||
|
- 'v*.*.*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update_major_minor_tags:
|
||||||
|
name: Make sure major and minor tags are up to date on a patch release
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Run Update semver
|
||||||
|
uses: haya14busa/action-update-semver@v1.2.1
|
99
.gitignore
vendored
Normal file
99
.gitignore
vendored
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# Dependency directory
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# OS metadata
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Ignore built ts files
|
||||||
|
__tests__/runner/*
|
||||||
|
lib/**/*
|
3
.prettierignore
Normal file
3
.prettierignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
dist/
|
||||||
|
lib/
|
||||||
|
node_modules/
|
10
.prettierrc.json
Normal file
10
.prettierrc.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": false,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"arrowParens": "avoid"
|
||||||
|
}
|
1
CODEOWNERS
Normal file
1
CODEOWNERS
Normal file
@ -0,0 +1 @@
|
|||||||
|
* @eifinger
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2024 Kevin Stillhammer
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
178
README.md
Normal file
178
README.md
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
# setup-uv
|
||||||
|
|
||||||
|
Set up your GitHub Actions workflow with a specific version of [uv](https://docs.astral.sh/uv/).
|
||||||
|
|
||||||
|
* Install a version of uv and add it to the path
|
||||||
|
* Cache the installed version of uv to speed up consecutive runs on self-hosted runners
|
||||||
|
* Register problem matchers for error output
|
||||||
|
* Optional: Cache the uv cache
|
||||||
|
* Optional: Verify the checksum of the downloaded uv executable
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
Example workflow will be added later
|
||||||
|
|
||||||
|
### Install specific version
|
||||||
|
|
||||||
|
You can also specify a specific version of uv
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Install a specific version
|
||||||
|
uses: eifinger/setup-uv@v1
|
||||||
|
with:
|
||||||
|
version: '0.3.0'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install latest version
|
||||||
|
|
||||||
|
By default this action installs the version defined as `default` in `action.yml`.
|
||||||
|
This gets automatically updated in a new release of this action when a new version of uv is released.
|
||||||
|
If you don't want to wait for a new release of this action you can use use `version: latest`.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> Using the `latest` version means that the uv executable gets downloaded every single time instead of loaded from the tools cache.
|
||||||
|
> This can take up to 20s depending on the download speed.
|
||||||
|
> This does not affect the uv cache.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Install a specific version
|
||||||
|
uses: eifinger/setup-uv@v1
|
||||||
|
with:
|
||||||
|
version: 'latest'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Validate checksum
|
||||||
|
|
||||||
|
You can also specify a checksum to validate the downloaded file.
|
||||||
|
Checksums up to the default version are automatically verified by this action.
|
||||||
|
The sha265 hashes can be found on the [releases page](https://github.com/astral-sh/uv/releases)
|
||||||
|
of the uv repo.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Install a specific version and validate the checksum
|
||||||
|
uses: eifinger/setup-uv@v1
|
||||||
|
with:
|
||||||
|
version: '0.3.1'
|
||||||
|
checksum: 'e11b01402ab645392c7ad6044db63d37e4fd1e745e015306993b07695ea5f9f8'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Enable caching
|
||||||
|
|
||||||
|
If you enable caching the [uv cache](https://docs.astral.sh/uv/concepts/cache/) will
|
||||||
|
be cached to the GitHub Actions Cache. This can speed up runs which can reuse the cache
|
||||||
|
by several minutes. The cache will always be reused on self-hosted runners.
|
||||||
|
|
||||||
|
You can optionally define a custom cache key suffix.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Enable caching and define a custom cache key suffix
|
||||||
|
id: setup-uv
|
||||||
|
uses: eifinger/setup-uv@v1
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
cache-suffix: 'optional-suffix'
|
||||||
|
```
|
||||||
|
|
||||||
|
When the cache was successfully restored the output `cache-hit` will be set to `true` and you can use it in subsequent steps.
|
||||||
|
For the example above you can use it like this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Do something if the cache was restored
|
||||||
|
if: steps.setup-uv.outputs.cache-hit == 'true'
|
||||||
|
run: echo "Cache was restored"
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Local cache path
|
||||||
|
|
||||||
|
If you want to save the cache to a local path other than the default path (`/tmp/setup-uv-cache`)
|
||||||
|
you can specify the path with the `cache-local-path` input.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Define a custom uv cache path
|
||||||
|
uses: eifinger/setup-uv@v1
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
cache-local-path: '/path/to/cache'
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cache dependency glob
|
||||||
|
|
||||||
|
If you want to control when the cache is invalidated you can specify a glob pattern with the `cache-dependency-glob` input.
|
||||||
|
The cache will be invalidated if any file matching the glob pattern changes.
|
||||||
|
The glob matches files relative to the repository root.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Define a custom cache dependency glob
|
||||||
|
uses: eifinger/setup-uv@v1
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
cache-dependency-glob: 'uv.lock'
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Define a custom cache dependency glob
|
||||||
|
uses: eifinger/setup-uv@v1
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
cache-dependency-glob: '**requirements*.txt'
|
||||||
|
```
|
||||||
|
|
||||||
|
### API rate limit
|
||||||
|
|
||||||
|
To avoid hitting the error `API rate limit exceeded` you can supply a GitHub token with the `github-token` input.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Install uv and supply a GitHub token
|
||||||
|
uses: eifinger/setup-uv@v1
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
```
|
||||||
|
|
||||||
|
## How it works
|
||||||
|
|
||||||
|
This action downloads uv from the releases of the [uv repo](https://github.com/astral-sh/uv) and uses the [GitHub Actions Toolkit](https://github.com/actions/toolkit) to cache it as a tool to speed up consecutive runs on self-hosted runners.
|
||||||
|
|
||||||
|
The installed version of uv is then added to the runner path so other steps can just use it by calling `uv`.
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
### Do I still need actions/setup-python when using this action?
|
||||||
|
|
||||||
|
No! This action was modelled as a drop-in replacement for `actions/setup-python` when using uv.
|
||||||
|
|
||||||
|
A simple example workflow could look like this:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@main
|
||||||
|
- name: Install the latest version of uv
|
||||||
|
uses: eifinger/setup-uv@v1
|
||||||
|
with:
|
||||||
|
enable-cache: true
|
||||||
|
- name: Sync dependencies
|
||||||
|
run: uv sync
|
||||||
|
- name: Test
|
||||||
|
run: uv run pytest
|
||||||
|
```
|
||||||
|
|
||||||
|
### What is the default version?
|
||||||
|
|
||||||
|
By default this action installs the version defined as `default` in `action.yml`.
|
||||||
|
When a new release of uv is published this triggers an automatic release of this action with the new version as `default`.
|
||||||
|
|
||||||
|
If you have to know the version installed for other steps of your workflow you can use the `uv-version` output:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: Checkout the repository
|
||||||
|
uses: actions/checkout@latest
|
||||||
|
- name: Install the default version of uv
|
||||||
|
id: setup-uv
|
||||||
|
uses: eifinger/setup-uv@v1
|
||||||
|
- name: Print the installed version
|
||||||
|
run: echo "Installed uv version is ${{ steps.setup-uv.outputs.uv-version }}"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
[<img src="https://raw.githubusercontent.com/eifinger/setup-uv/main/docs/images/bmc-button.svg" width=150 height=40 style="margin: 5px"/>](https://www.buymeacoffee.com/eifinger)
|
||||||
|
[<img src="https://raw.githubusercontent.com/eifinger/setup-uv/main/docs/images/paypal-button.svg" width=150 height=40 style="margin: 5px"/>](https://paypal.me/kevinstillhammer)
|
37
__tests__/download/checksum/checkstum.test.ts
Normal file
37
__tests__/download/checksum/checkstum.test.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import {expect, test, it} from '@jest/globals'
|
||||||
|
import {
|
||||||
|
isknownVersion,
|
||||||
|
validateChecksum
|
||||||
|
} from '../../../src/download/checksum/checksum'
|
||||||
|
|
||||||
|
test('checksum should match', async () => {
|
||||||
|
const validChecksum =
|
||||||
|
'f3da96ec7e995debee7f5d52ecd034dfb7074309a1da42f76429ecb814d813a3'
|
||||||
|
const filePath = '__tests__/fixtures/checksumfile'
|
||||||
|
// string params don't matter only test the checksum mechanism, not known checksums
|
||||||
|
await validateChecksum(
|
||||||
|
validChecksum,
|
||||||
|
filePath,
|
||||||
|
'aarch64',
|
||||||
|
'pc-windows-msvc',
|
||||||
|
'1.2.3'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
type KnownVersionFixture = {version: string; known: boolean}
|
||||||
|
|
||||||
|
it.each<KnownVersionFixture>([
|
||||||
|
{
|
||||||
|
version: '0.3.0',
|
||||||
|
known: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
version: '0.0.15',
|
||||||
|
known: false
|
||||||
|
}
|
||||||
|
])(
|
||||||
|
'isknownVersion should return $known for version $version',
|
||||||
|
({version, known}) => {
|
||||||
|
expect(isknownVersion(version)).toBe(known)
|
||||||
|
}
|
||||||
|
)
|
1
__tests__/fixtures/checksumfile
Normal file
1
__tests__/fixtures/checksumfile
Normal file
@ -0,0 +1 @@
|
|||||||
|
Random file content
|
0
__tests__/fixtures/uv-project/README.md
Normal file
0
__tests__/fixtures/uv-project/README.md
Normal file
13
__tests__/fixtures/uv-project/pyproject.toml
Normal file
13
__tests__/fixtures/uv-project/pyproject.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[project]
|
||||||
|
name = "uv-project"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
dependencies = [
|
||||||
|
"ruff>=0.6.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[build-system]
|
||||||
|
requires = ["hatchling"]
|
||||||
|
build-backend = "hatchling.build"
|
2
__tests__/fixtures/uv-project/src/uv_project/__init__.py
Normal file
2
__tests__/fixtures/uv-project/src/uv_project/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
def hello() -> str:
|
||||||
|
return "Hello from uv-project!"
|
38
__tests__/fixtures/uv-project/uv.lock
generated
Normal file
38
__tests__/fixtures/uv-project/uv.lock
generated
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
version = 1
|
||||||
|
requires-python = ">=3.12"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ruff"
|
||||||
|
version = "0.6.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/23/f4/279d044f66b79261fd37df76bf72b64471afab5d3b7906a01499c4451910/ruff-0.6.2.tar.gz", hash = "sha256:239ee6beb9e91feb8e0ec384204a763f36cb53fb895a1a364618c6abb076b3be", size = 2460281 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/72/4b/47dd7a69287afb4069fa42c198e899463605460a58120196711bfcf0446b/ruff-0.6.2-py3-none-linux_armv6l.whl", hash = "sha256:5c8cbc6252deb3ea840ad6a20b0f8583caab0c5ef4f9cca21adc5a92b8f79f3c", size = 9695871 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ae/c3/8aac62ac4638c14a740ee76a755a925f2d0d04580ab790a9887accb729f6/ruff-0.6.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:17002fe241e76544448a8e1e6118abecbe8cd10cf68fde635dad480dba594570", size = 9459354 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2f/cf/77fbd8d4617b9b9c503f9bffb8552c4e3ea1a58dc36975e7a9104ffb0f85/ruff-0.6.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3dbeac76ed13456f8158b8f4fe087bf87882e645c8e8b606dd17b0b66c2c1158", size = 9163871 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/1c/765192bab32b79efbb498b06f0b9dcb3629112b53b8777ae1d19b8209e09/ruff-0.6.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:094600ee88cda325988d3f54e3588c46de5c18dae09d683ace278b11f9d4d534", size = 10096250 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/08/d0/86f3cb0f6934c99f759c232984a5204d67a26745cad2d9edff6248adf7d2/ruff-0.6.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:316d418fe258c036ba05fbf7dfc1f7d3d4096db63431546163b472285668132b", size = 9475376 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cd/cc/4c8d0e225b559a3fae6092ec310d7150d3b02b4669e9223f783ef64d82c0/ruff-0.6.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d72b8b3abf8a2d51b7b9944a41307d2f442558ccb3859bbd87e6ae9be1694a5d", size = 10295634 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/db/96/d2699cfb1bb5a01c68122af43454c76c31331e1c8a9bd97d653d7c82524b/ruff-0.6.2-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:2aed7e243be68487aa8982e91c6e260982d00da3f38955873aecd5a9204b1d66", size = 11024941 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8b/a9/6ecd66af8929e0f2a1ed308a4137f3521789f28f0eb97d32c2ca3aa7000c/ruff-0.6.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d371f7fc9cec83497fe7cf5eaf5b76e22a8efce463de5f775a1826197feb9df8", size = 10606894 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/73/2ee4cd19f44992fedac1cc6db9e3d825966072f6dcbd4032f21cbd063170/ruff-0.6.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8f310d63af08f583363dfb844ba8f9417b558199c58a5999215082036d795a1", size = 11552886 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/60/4c/c0f1cd35ce4a93c54a6bb1ee6934a3a205fa02198dd076678193853ceea1/ruff-0.6.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7db6880c53c56addb8638fe444818183385ec85eeada1d48fc5abe045301b2f1", size = 10264945 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/89/e45c9359b9cdd4245512ea2b9f2bb128a997feaa5f726fc9e8c7a66afadf/ruff-0.6.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:1175d39faadd9a50718f478d23bfc1d4da5743f1ab56af81a2b6caf0a2394f23", size = 10100007 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/06/74/0bd4e0a7ed5f6908df87892f9bf60a2356c0fd74102d8097298bd9b4f346/ruff-0.6.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:5b939f9c86d51635fe486585389f54582f0d65b8238e08c327c1534844b3bb9a", size = 9559267 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/54/03/3dc6dc9419f276f05805bf888c279e3e0b631284abd548d9e87cebb93aec/ruff-0.6.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d0d62ca91219f906caf9b187dea50d17353f15ec9bb15aae4a606cd697b49b4c", size = 9905304 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5c/5b/d6a72a6a6bbf097c09de468326ef5fa1c9e7aa5e6e45979bc0d984b0dbe7/ruff-0.6.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:7438a7288f9d67ed3c8ce4d059e67f7ed65e9fe3aa2ab6f5b4b3610e57e3cb56", size = 10341480 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/79/a9/0f2f21fe15ba537c46598f96aa9ae4a3d4b9ec64926664617ca6a8c772f4/ruff-0.6.2-py3-none-win32.whl", hash = "sha256:279d5f7d86696df5f9549b56b9b6a7f6c72961b619022b5b7999b15db392a4da", size = 7961901 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/80/fff12ffe11853d9f4ea3e5221e6dd2e93640a161c05c9579833e09ad40a7/ruff-0.6.2-py3-none-win_amd64.whl", hash = "sha256:d9f3469c7dd43cd22eb1c3fc16926fb8258d50cb1b216658a07be95dd117b0f2", size = 8783320 },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/56/91/577cdd64cce5e74d3f8b5ecb93f29566def569c741eb008aed4f331ef821/ruff-0.6.2-py3-none-win_arm64.whl", hash = "sha256:f28fcd2cd0e02bdf739297516d5643a945cc7caf09bd9bcb4d932540a5ea4fa9", size = 8225886 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uv-project"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { editable = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "ruff" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [{ name = "ruff" }]
|
38
action.yml
Normal file
38
action.yml
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
name: 'Python setup uv'
|
||||||
|
description: 'Set up your GitHub Actions workflow with a specific version of uv'
|
||||||
|
author: 'eifinger'
|
||||||
|
inputs:
|
||||||
|
version:
|
||||||
|
description: 'The version of uv to install'
|
||||||
|
default: '0.3.2'
|
||||||
|
checksum:
|
||||||
|
description: 'The checksum of the uv version to install'
|
||||||
|
required: false
|
||||||
|
github-token:
|
||||||
|
description: 'Used to increase the rate limit when retrieving versions and downloading uv.'
|
||||||
|
required: false
|
||||||
|
enable-cache:
|
||||||
|
description: 'Enable caching of the uv cache'
|
||||||
|
default: 'false'
|
||||||
|
cache-dependency-glob:
|
||||||
|
description: 'Glob pattern to match files relative to the repository root to control the cache. e.g. "uv.lock"'
|
||||||
|
required: false
|
||||||
|
cache-suffix:
|
||||||
|
description: 'Suffix for the cache key'
|
||||||
|
required: false
|
||||||
|
cache-local-path:
|
||||||
|
description: 'Local path to store the cache.'
|
||||||
|
default: '/tmp/setup-uv-cache'
|
||||||
|
outputs:
|
||||||
|
uv-version:
|
||||||
|
description: "The installed uv version. Useful when using latest."
|
||||||
|
cache-hit:
|
||||||
|
description: "A boolean value to indicate a cache entry was found"
|
||||||
|
runs:
|
||||||
|
using: 'node20'
|
||||||
|
main: 'dist/setup/index.js'
|
||||||
|
post: 'dist/save-cache/index.js'
|
||||||
|
post-if: success()
|
||||||
|
branding:
|
||||||
|
icon: 'package'
|
||||||
|
color: 'blue'
|
84943
dist/save-cache/index.js
generated
vendored
Normal file
84943
dist/save-cache/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/save-cache/index.js.map
generated
vendored
Normal file
1
dist/save-cache/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/save-cache/sourcemap-register.js
generated
vendored
Normal file
1
dist/save-cache/sourcemap-register.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
453
dist/setup/37.index.js
generated
vendored
Normal file
453
dist/setup/37.index.js
generated
vendored
Normal file
@ -0,0 +1,453 @@
|
|||||||
|
"use strict";
|
||||||
|
exports.id = 37;
|
||||||
|
exports.ids = [37];
|
||||||
|
exports.modules = {
|
||||||
|
|
||||||
|
/***/ 4037:
|
||||||
|
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
||||||
|
|
||||||
|
__webpack_require__.r(__webpack_exports__);
|
||||||
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
||||||
|
/* harmony export */ "toFormData": () => (/* binding */ toFormData)
|
||||||
|
/* harmony export */ });
|
||||||
|
/* harmony import */ var fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2777);
|
||||||
|
/* harmony import */ var formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8010);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let s = 0;
|
||||||
|
const S = {
|
||||||
|
START_BOUNDARY: s++,
|
||||||
|
HEADER_FIELD_START: s++,
|
||||||
|
HEADER_FIELD: s++,
|
||||||
|
HEADER_VALUE_START: s++,
|
||||||
|
HEADER_VALUE: s++,
|
||||||
|
HEADER_VALUE_ALMOST_DONE: s++,
|
||||||
|
HEADERS_ALMOST_DONE: s++,
|
||||||
|
PART_DATA_START: s++,
|
||||||
|
PART_DATA: s++,
|
||||||
|
END: s++
|
||||||
|
};
|
||||||
|
|
||||||
|
let f = 1;
|
||||||
|
const F = {
|
||||||
|
PART_BOUNDARY: f,
|
||||||
|
LAST_BOUNDARY: f *= 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const LF = 10;
|
||||||
|
const CR = 13;
|
||||||
|
const SPACE = 32;
|
||||||
|
const HYPHEN = 45;
|
||||||
|
const COLON = 58;
|
||||||
|
const A = 97;
|
||||||
|
const Z = 122;
|
||||||
|
|
||||||
|
const lower = c => c | 0x20;
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
|
||||||
|
class MultipartParser {
|
||||||
|
/**
|
||||||
|
* @param {string} boundary
|
||||||
|
*/
|
||||||
|
constructor(boundary) {
|
||||||
|
this.index = 0;
|
||||||
|
this.flags = 0;
|
||||||
|
|
||||||
|
this.onHeaderEnd = noop;
|
||||||
|
this.onHeaderField = noop;
|
||||||
|
this.onHeadersEnd = noop;
|
||||||
|
this.onHeaderValue = noop;
|
||||||
|
this.onPartBegin = noop;
|
||||||
|
this.onPartData = noop;
|
||||||
|
this.onPartEnd = noop;
|
||||||
|
|
||||||
|
this.boundaryChars = {};
|
||||||
|
|
||||||
|
boundary = '\r\n--' + boundary;
|
||||||
|
const ui8a = new Uint8Array(boundary.length);
|
||||||
|
for (let i = 0; i < boundary.length; i++) {
|
||||||
|
ui8a[i] = boundary.charCodeAt(i);
|
||||||
|
this.boundaryChars[ui8a[i]] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.boundary = ui8a;
|
||||||
|
this.lookbehind = new Uint8Array(this.boundary.length + 8);
|
||||||
|
this.state = S.START_BOUNDARY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} data
|
||||||
|
*/
|
||||||
|
write(data) {
|
||||||
|
let i = 0;
|
||||||
|
const length_ = data.length;
|
||||||
|
let previousIndex = this.index;
|
||||||
|
let {lookbehind, boundary, boundaryChars, index, state, flags} = this;
|
||||||
|
const boundaryLength = this.boundary.length;
|
||||||
|
const boundaryEnd = boundaryLength - 1;
|
||||||
|
const bufferLength = data.length;
|
||||||
|
let c;
|
||||||
|
let cl;
|
||||||
|
|
||||||
|
const mark = name => {
|
||||||
|
this[name + 'Mark'] = i;
|
||||||
|
};
|
||||||
|
|
||||||
|
const clear = name => {
|
||||||
|
delete this[name + 'Mark'];
|
||||||
|
};
|
||||||
|
|
||||||
|
const callback = (callbackSymbol, start, end, ui8a) => {
|
||||||
|
if (start === undefined || start !== end) {
|
||||||
|
this[callbackSymbol](ui8a && ui8a.subarray(start, end));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const dataCallback = (name, clear) => {
|
||||||
|
const markSymbol = name + 'Mark';
|
||||||
|
if (!(markSymbol in this)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clear) {
|
||||||
|
callback(name, this[markSymbol], i, data);
|
||||||
|
delete this[markSymbol];
|
||||||
|
} else {
|
||||||
|
callback(name, this[markSymbol], data.length, data);
|
||||||
|
this[markSymbol] = 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (i = 0; i < length_; i++) {
|
||||||
|
c = data[i];
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case S.START_BOUNDARY:
|
||||||
|
if (index === boundary.length - 2) {
|
||||||
|
if (c === HYPHEN) {
|
||||||
|
flags |= F.LAST_BOUNDARY;
|
||||||
|
} else if (c !== CR) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
break;
|
||||||
|
} else if (index - 1 === boundary.length - 2) {
|
||||||
|
if (flags & F.LAST_BOUNDARY && c === HYPHEN) {
|
||||||
|
state = S.END;
|
||||||
|
flags = 0;
|
||||||
|
} else if (!(flags & F.LAST_BOUNDARY) && c === LF) {
|
||||||
|
index = 0;
|
||||||
|
callback('onPartBegin');
|
||||||
|
state = S.HEADER_FIELD_START;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c !== boundary[index + 2]) {
|
||||||
|
index = -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c === boundary[index + 2]) {
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case S.HEADER_FIELD_START:
|
||||||
|
state = S.HEADER_FIELD;
|
||||||
|
mark('onHeaderField');
|
||||||
|
index = 0;
|
||||||
|
// falls through
|
||||||
|
case S.HEADER_FIELD:
|
||||||
|
if (c === CR) {
|
||||||
|
clear('onHeaderField');
|
||||||
|
state = S.HEADERS_ALMOST_DONE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
if (c === HYPHEN) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (c === COLON) {
|
||||||
|
if (index === 1) {
|
||||||
|
// empty header field
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dataCallback('onHeaderField', true);
|
||||||
|
state = S.HEADER_VALUE_START;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
cl = lower(c);
|
||||||
|
if (cl < A || cl > Z) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case S.HEADER_VALUE_START:
|
||||||
|
if (c === SPACE) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
mark('onHeaderValue');
|
||||||
|
state = S.HEADER_VALUE;
|
||||||
|
// falls through
|
||||||
|
case S.HEADER_VALUE:
|
||||||
|
if (c === CR) {
|
||||||
|
dataCallback('onHeaderValue', true);
|
||||||
|
callback('onHeaderEnd');
|
||||||
|
state = S.HEADER_VALUE_ALMOST_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case S.HEADER_VALUE_ALMOST_DONE:
|
||||||
|
if (c !== LF) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = S.HEADER_FIELD_START;
|
||||||
|
break;
|
||||||
|
case S.HEADERS_ALMOST_DONE:
|
||||||
|
if (c !== LF) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
callback('onHeadersEnd');
|
||||||
|
state = S.PART_DATA_START;
|
||||||
|
break;
|
||||||
|
case S.PART_DATA_START:
|
||||||
|
state = S.PART_DATA;
|
||||||
|
mark('onPartData');
|
||||||
|
// falls through
|
||||||
|
case S.PART_DATA:
|
||||||
|
previousIndex = index;
|
||||||
|
|
||||||
|
if (index === 0) {
|
||||||
|
// boyer-moore derrived algorithm to safely skip non-boundary data
|
||||||
|
i += boundaryEnd;
|
||||||
|
while (i < bufferLength && !(data[i] in boundaryChars)) {
|
||||||
|
i += boundaryLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
i -= boundaryEnd;
|
||||||
|
c = data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index < boundary.length) {
|
||||||
|
if (boundary[index] === c) {
|
||||||
|
if (index === 0) {
|
||||||
|
dataCallback('onPartData', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
} else {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
} else if (index === boundary.length) {
|
||||||
|
index++;
|
||||||
|
if (c === CR) {
|
||||||
|
// CR = part boundary
|
||||||
|
flags |= F.PART_BOUNDARY;
|
||||||
|
} else if (c === HYPHEN) {
|
||||||
|
// HYPHEN = end boundary
|
||||||
|
flags |= F.LAST_BOUNDARY;
|
||||||
|
} else {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
} else if (index - 1 === boundary.length) {
|
||||||
|
if (flags & F.PART_BOUNDARY) {
|
||||||
|
index = 0;
|
||||||
|
if (c === LF) {
|
||||||
|
// unset the PART_BOUNDARY flag
|
||||||
|
flags &= ~F.PART_BOUNDARY;
|
||||||
|
callback('onPartEnd');
|
||||||
|
callback('onPartBegin');
|
||||||
|
state = S.HEADER_FIELD_START;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (flags & F.LAST_BOUNDARY) {
|
||||||
|
if (c === HYPHEN) {
|
||||||
|
callback('onPartEnd');
|
||||||
|
state = S.END;
|
||||||
|
flags = 0;
|
||||||
|
} else {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index > 0) {
|
||||||
|
// when matching a possible boundary, keep a lookbehind reference
|
||||||
|
// in case it turns out to be a false lead
|
||||||
|
lookbehind[index - 1] = c;
|
||||||
|
} else if (previousIndex > 0) {
|
||||||
|
// if our boundary turned out to be rubbish, the captured lookbehind
|
||||||
|
// belongs to partData
|
||||||
|
const _lookbehind = new Uint8Array(lookbehind.buffer, lookbehind.byteOffset, lookbehind.byteLength);
|
||||||
|
callback('onPartData', 0, previousIndex, _lookbehind);
|
||||||
|
previousIndex = 0;
|
||||||
|
mark('onPartData');
|
||||||
|
|
||||||
|
// reconsider the current character even so it interrupted the sequence
|
||||||
|
// it could be the beginning of a new sequence
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case S.END:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unexpected state entered: ${state}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataCallback('onHeaderField');
|
||||||
|
dataCallback('onHeaderValue');
|
||||||
|
dataCallback('onPartData');
|
||||||
|
|
||||||
|
// Update properties for the next call
|
||||||
|
this.index = index;
|
||||||
|
this.state = state;
|
||||||
|
this.flags = flags;
|
||||||
|
}
|
||||||
|
|
||||||
|
end() {
|
||||||
|
if ((this.state === S.HEADER_FIELD_START && this.index === 0) ||
|
||||||
|
(this.state === S.PART_DATA && this.index === this.boundary.length)) {
|
||||||
|
this.onPartEnd();
|
||||||
|
} else if (this.state !== S.END) {
|
||||||
|
throw new Error('MultipartParser.end(): stream ended unexpectedly');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _fileName(headerValue) {
|
||||||
|
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
|
||||||
|
const m = headerValue.match(/\bfilename=("(.*?)"|([^()<>@,;:\\"/[\]?={}\s\t]+))($|;\s)/i);
|
||||||
|
if (!m) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const match = m[2] || m[3] || '';
|
||||||
|
let filename = match.slice(match.lastIndexOf('\\') + 1);
|
||||||
|
filename = filename.replace(/%22/g, '"');
|
||||||
|
filename = filename.replace(/&#(\d{4});/g, (m, code) => {
|
||||||
|
return String.fromCharCode(code);
|
||||||
|
});
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function toFormData(Body, ct) {
|
||||||
|
if (!/multipart/i.test(ct)) {
|
||||||
|
throw new TypeError('Failed to fetch');
|
||||||
|
}
|
||||||
|
|
||||||
|
const m = ct.match(/boundary=(?:"([^"]+)"|([^;]+))/i);
|
||||||
|
|
||||||
|
if (!m) {
|
||||||
|
throw new TypeError('no or bad content-type header, no multipart boundary');
|
||||||
|
}
|
||||||
|
|
||||||
|
const parser = new MultipartParser(m[1] || m[2]);
|
||||||
|
|
||||||
|
let headerField;
|
||||||
|
let headerValue;
|
||||||
|
let entryValue;
|
||||||
|
let entryName;
|
||||||
|
let contentType;
|
||||||
|
let filename;
|
||||||
|
const entryChunks = [];
|
||||||
|
const formData = new formdata_polyfill_esm_min_js__WEBPACK_IMPORTED_MODULE_1__/* .FormData */ .Ct();
|
||||||
|
|
||||||
|
const onPartData = ui8a => {
|
||||||
|
entryValue += decoder.decode(ui8a, {stream: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendToFile = ui8a => {
|
||||||
|
entryChunks.push(ui8a);
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendFileToFormData = () => {
|
||||||
|
const file = new fetch_blob_from_js__WEBPACK_IMPORTED_MODULE_0__/* .File */ .$B(entryChunks, filename, {type: contentType});
|
||||||
|
formData.append(entryName, file);
|
||||||
|
};
|
||||||
|
|
||||||
|
const appendEntryToFormData = () => {
|
||||||
|
formData.append(entryName, entryValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const decoder = new TextDecoder('utf-8');
|
||||||
|
decoder.decode();
|
||||||
|
|
||||||
|
parser.onPartBegin = function () {
|
||||||
|
parser.onPartData = onPartData;
|
||||||
|
parser.onPartEnd = appendEntryToFormData;
|
||||||
|
|
||||||
|
headerField = '';
|
||||||
|
headerValue = '';
|
||||||
|
entryValue = '';
|
||||||
|
entryName = '';
|
||||||
|
contentType = '';
|
||||||
|
filename = null;
|
||||||
|
entryChunks.length = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
parser.onHeaderField = function (ui8a) {
|
||||||
|
headerField += decoder.decode(ui8a, {stream: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
parser.onHeaderValue = function (ui8a) {
|
||||||
|
headerValue += decoder.decode(ui8a, {stream: true});
|
||||||
|
};
|
||||||
|
|
||||||
|
parser.onHeaderEnd = function () {
|
||||||
|
headerValue += decoder.decode();
|
||||||
|
headerField = headerField.toLowerCase();
|
||||||
|
|
||||||
|
if (headerField === 'content-disposition') {
|
||||||
|
// matches either a quoted-string or a token (RFC 2616 section 19.5.1)
|
||||||
|
const m = headerValue.match(/\bname=("([^"]*)"|([^()<>@,;:\\"/[\]?={}\s\t]+))/i);
|
||||||
|
|
||||||
|
if (m) {
|
||||||
|
entryName = m[2] || m[3] || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
filename = _fileName(headerValue);
|
||||||
|
|
||||||
|
if (filename) {
|
||||||
|
parser.onPartData = appendToFile;
|
||||||
|
parser.onPartEnd = appendFileToFormData;
|
||||||
|
}
|
||||||
|
} else if (headerField === 'content-type') {
|
||||||
|
contentType = headerValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
headerValue = '';
|
||||||
|
headerField = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
for await (const chunk of Body) {
|
||||||
|
parser.write(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
parser.end();
|
||||||
|
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***/ })
|
||||||
|
|
||||||
|
};
|
||||||
|
;
|
||||||
|
//# sourceMappingURL=37.index.js.map
|
1
dist/setup/37.index.js.map
generated
vendored
Normal file
1
dist/setup/37.index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
87675
dist/setup/index.js
generated
vendored
Normal file
87675
dist/setup/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/setup/index.js.map
generated
vendored
Normal file
1
dist/setup/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1056
dist/setup/licenses.txt
generated
vendored
Normal file
1056
dist/setup/licenses.txt
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1
dist/setup/sourcemap-register.js
generated
vendored
Normal file
1
dist/setup/sourcemap-register.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
35031
dist/update-checksums/index.js
generated
vendored
Normal file
35031
dist/update-checksums/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/update-checksums/index.js.map
generated
vendored
Normal file
1
dist/update-checksums/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/update-checksums/sourcemap-register.js
generated
vendored
Normal file
1
dist/update-checksums/sourcemap-register.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
34791
dist/update-default-version/index.js
generated
vendored
Normal file
34791
dist/update-default-version/index.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/update-default-version/index.js.map
generated
vendored
Normal file
1
dist/update-default-version/index.js.map
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dist/update-default-version/sourcemap-register.js
generated
vendored
Normal file
1
dist/update-default-version/sourcemap-register.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
22
docs/images/bmc-button.svg
Normal file
22
docs/images/bmc-button.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 36 KiB |
15
docs/images/paypal-button.svg
Normal file
15
docs/images/paypal-button.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="545" height="153" viewBox="0 0 545 153">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1{fill:#009ee3;}.cls-1,.cls-2,.cls-3{fill-rule:evenodd;}.cls-2{fill:#113984;}.cls-3{fill:#172c70;}</style>
|
||||||
|
</defs>
|
||||||
|
<title>paypal-seeklogo.com</title>
|
||||||
|
<path transform="scale(1, 1)" d="M0 24.48C0 10.9601 10.9601 0 24.48 0H520.2C533.72 0 544.68 10.9601 544.68 24.48V128.52C544.68 142.04 533.72 153 520.2 153H24.48C10.9601 153 0 142.04 0 128.52V24.48Z" fill="#ebf2ff"/>
|
||||||
|
<g transform="scale(0.8, 0.8) translate(45, 25)">
|
||||||
|
<path class="cls-1" d="M192.95,386.87h38.74c20.8,0,28.63,10.53,27.42,26-2,25.54-17.44,39.67-37.92,39.67H210.85c-2.81,0-4.7,1.86-5.46,6.9L201,488.74c-0.29,1.9-1.29,3-2.79,3.15H173.87c-2.29,0-3.1-1.75-2.5-5.54l14.84-93.93C186.79,388.66,188.85,386.87,192.95,386.87Z" transform="translate(-143.48 -354.54)"/>
|
||||||
|
<path class="cls-2" d="M361.14,385.13c13.07,0,25.13,7.09,23.48,24.76-2,21-13.25,32.62-31,32.67H338.11c-2.23,0-3.31,1.82-3.89,5.55l-3,19.07c-0.45,2.88-1.93,4.3-4.11,4.3H312.68c-2.3,0-3.1-1.47-2.59-4.76L322,390.29c0.59-3.76,2-5.16,4.57-5.16h34.54Zm-23.5,40.92h11.75c7.35-.28,12.23-5.37,12.72-14.55,0.3-5.67-3.53-9.73-9.62-9.7l-11.06.05-3.79,24.2h0Zm86.21,39.58c1.32-1.2,2.66-1.82,2.47-.34l-0.47,3.54c-0.24,1.85.49,2.83,2.21,2.83h12.82c2.16,0,3.21-.87,3.74-4.21l7.9-49.58c0.4-2.49-.21-3.71-2.1-3.71H436.32c-1.27,0-1.89.71-2.22,2.65l-0.52,3.05c-0.27,1.59-1,1.87-1.68.27-2.39-5.66-8.49-8.2-17-8-19.77.41-33.1,15.42-34.53,34.66-1.1,14.88,9.56,26.57,23.62,26.57,10.2,0,14.76-3,19.9-7.7h0ZM413.11,458c-8.51,0-14.44-6.79-13.21-15.11s9.19-15.11,17.7-15.11,14.44,6.79,13.21,15.11S421.63,458,413.11,458h0Zm64.5-44h-13c-2.68,0-3.77,2-2.92,4.46l16.14,47.26L462,488.21c-1.33,1.88-.3,3.59,1.57,3.59h14.61a4.47,4.47,0,0,0,4.34-2.13l49.64-71.2c1.53-2.19.81-4.49-1.7-4.49H516.63c-2.37,0-3.32.94-4.68,2.91l-20.7,30L482,416.82C481.46,415,480.11,414,477.62,414Z" transform="translate(-143.48 -354.54)"/>
|
||||||
|
<path class="cls-1" d="M583.8,385.13c13.07,0,25.13,7.09,23.48,24.76-2,21-13.25,32.62-31,32.67H560.78c-2.23,0-3.31,1.82-3.89,5.55l-3,19.07c-0.45,2.88-1.93,4.3-4.11,4.3H535.35c-2.3,0-3.1-1.47-2.59-4.76l11.93-76.45c0.59-3.76,2-5.16,4.57-5.16H583.8Zm-23.5,40.92h11.75c7.35-.28,12.23-5.37,12.72-14.55,0.3-5.67-3.53-9.73-9.62-9.7l-11.06.05-3.79,24.2h0Zm86.21,39.58c1.32-1.2,2.66-1.82,2.47-.34l-0.47,3.54c-0.24,1.85.49,2.83,2.21,2.83h12.82c2.16,0,3.21-.87,3.74-4.21l7.9-49.58c0.4-2.49-.21-3.71-2.1-3.71H659c-1.27,0-1.89.71-2.22,2.65l-0.52,3.05c-0.27,1.59-1,1.87-1.68.27-2.39-5.66-8.49-8.2-17-8-19.77.41-33.1,15.42-34.53,34.66-1.1,14.88,9.56,26.57,23.62,26.57,10.2,0,14.76-3,19.9-7.7h0ZM635.78,458c-8.51,0-14.44-6.79-13.21-15.11s9.19-15.11,17.7-15.11,14.44,6.79,13.21,15.11S644.29,458,635.78,458h0Zm59.13,13.74h-14.8a1.75,1.75,0,0,1-1.81-2l13-82.36a2.55,2.55,0,0,1,2.46-2h14.8a1.75,1.75,0,0,1,1.81,2l-13,82.36A2.55,2.55,0,0,1,694.91,471.76Z" transform="translate(-143.48 -354.54)"/>
|
||||||
|
<path class="cls-2" d="M168.72,354.54h38.78c10.92,0,23.88.35,32.54,8,5.79,5.11,8.83,13.24,8.13,22-2.38,29.61-20.09,46.2-43.85,46.2H185.2c-3.26,0-5.41,2.16-6.33,8l-5.34,34c-0.35,2.2-1.3,3.5-3,3.66H146.6c-2.65,0-3.59-2-2.9-6.42L160.9,361C161.59,356.62,164,354.54,168.72,354.54Z" transform="translate(-143.48 -354.54)"/>
|
||||||
|
<path class="cls-3" d="M179.43,435.29l6.77-42.87c0.59-3.76,2.65-5.56,6.75-5.56h38.74c6.41,0,11.6,1,15.66,2.85-3.89,26.36-20.94,41-43.26,41H185C182.44,430.72,180.56,432,179.43,435.29Z" transform="translate(-143.48 -354.54)"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.4 KiB |
9
jest.config.js
Normal file
9
jest.config.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
module.exports = {
|
||||||
|
clearMocks: true,
|
||||||
|
moduleFileExtensions: ['js', 'ts'],
|
||||||
|
testMatch: ['**/*.test.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': 'ts-jest'
|
||||||
|
},
|
||||||
|
verbose: true
|
||||||
|
}
|
13254
package-lock.json
generated
Normal file
13254
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
57
package.json
Normal file
57
package.json
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"name": "setup-uv",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
|
"description": "Set up your GitHub Actions workflow with a specific version of uv",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"format": "prettier --write '**/*.ts'",
|
||||||
|
"format-check": "prettier --check '**/*.ts'",
|
||||||
|
"lint": "eslint src/**/*.ts --fix",
|
||||||
|
"package": "ncc build -o dist/setup src/setup-uv.ts && ncc build -o dist/save-cache src/save-cache.ts && ncc build -o dist/update-default-version src/update-default-version.ts",
|
||||||
|
"test": "jest",
|
||||||
|
"act": "act pull_request -W .github/workflows/test.yml --container-architecture linux/amd64 -s GITHUB_TOKEN=\"$(gh auth token)\"",
|
||||||
|
"update-default-version": "node dist/update-default-version/index.js src/download/checksum/known-checksums.ts action.yml \"$(gh auth token)\"",
|
||||||
|
"all": "npm run build && npm run format && npm run lint && npm run package && npm test"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/eifinger/setup-uv.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"actions",
|
||||||
|
"python",
|
||||||
|
"setup",
|
||||||
|
"uv"
|
||||||
|
],
|
||||||
|
"author": "@eifinger",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/cache": "^3.2.4",
|
||||||
|
"@actions/core": "^1.10.1",
|
||||||
|
"@actions/exec": "^1.1.1",
|
||||||
|
"@actions/github": "^6.0.0",
|
||||||
|
"@actions/glob": "^0.4.0",
|
||||||
|
"@actions/io": "^1.1.3",
|
||||||
|
"@actions/tool-cache": "^2.0.1",
|
||||||
|
"@octokit/rest": "^20.1.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.14.9",
|
||||||
|
"@types/semver": "^7.5.8",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^7.15.0",
|
||||||
|
"@typescript-eslint/parser": "^7.15.0",
|
||||||
|
"@vercel/ncc": "^0.38.1",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-github": "^4.10.2",
|
||||||
|
"eslint-plugin-import": "^2.29.1",
|
||||||
|
"eslint-plugin-jest": "^28.6.0",
|
||||||
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
|
"jest": "^29.7.0",
|
||||||
|
"js-yaml": "^4.1.0",
|
||||||
|
"prettier": "^3.3.2",
|
||||||
|
"ts-jest": "^29.1.5",
|
||||||
|
"typescript": "^5.4.5"
|
||||||
|
}
|
||||||
|
}
|
63
src/cache/restore-cache.ts
vendored
Normal file
63
src/cache/restore-cache.ts
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import * as cache from '@actions/cache'
|
||||||
|
import * as glob from '@actions/glob'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import path from 'path'
|
||||||
|
import {cacheDependencyGlob, cacheLocalPath, cacheSuffix} from '../utils/inputs'
|
||||||
|
import {getArch, getPlatform} from '../utils/platforms'
|
||||||
|
|
||||||
|
export const STATE_CACHE_KEY = 'cache-key'
|
||||||
|
export const STATE_CACHE_MATCHED_KEY = 'cache-matched-key'
|
||||||
|
const CACHE_VERSION = '1'
|
||||||
|
const fullCacheDependencyGlob = `${process.env['GITHUB_WORKSPACE']}${path.sep}${cacheDependencyGlob}`
|
||||||
|
|
||||||
|
export async function restoreCache(version: string): Promise<void> {
|
||||||
|
const cacheKey = await computeKeys(version)
|
||||||
|
|
||||||
|
let matchedKey: string | undefined
|
||||||
|
core.info(
|
||||||
|
`Trying to restore uv cache from GitHub Actions cache with key: ${cacheKey}`
|
||||||
|
)
|
||||||
|
try {
|
||||||
|
matchedKey = await cache.restoreCache([cacheLocalPath], cacheKey)
|
||||||
|
} catch (err) {
|
||||||
|
const message = (err as Error).message
|
||||||
|
core.warning(message)
|
||||||
|
core.setOutput('cache-hit', false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
core.saveState(STATE_CACHE_KEY, cacheKey)
|
||||||
|
|
||||||
|
handleMatchResult(matchedKey, cacheKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function computeKeys(version: string): Promise<string> {
|
||||||
|
let cacheDependencyPathHash = '-'
|
||||||
|
if (fullCacheDependencyGlob !== '') {
|
||||||
|
cacheDependencyPathHash += await glob.hashFiles(fullCacheDependencyGlob)
|
||||||
|
if (cacheDependencyPathHash === '-') {
|
||||||
|
throw new Error(
|
||||||
|
`No file in ${process.cwd()} matched to [${cacheDependencyGlob}], make sure you have checked out the target repository`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const suffix = cacheSuffix ? `-${cacheSuffix}` : ''
|
||||||
|
return `setup-uv-${CACHE_VERSION}-${getArch()}-${getPlatform()}-${version}${cacheDependencyPathHash}${suffix}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleMatchResult(
|
||||||
|
matchedKey: string | undefined,
|
||||||
|
primaryKey: string
|
||||||
|
): void {
|
||||||
|
if (!matchedKey) {
|
||||||
|
core.info(`No GitHub Actions cache found for key: ${primaryKey}`)
|
||||||
|
core.setOutput('cache-hit', false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
core.saveState(STATE_CACHE_MATCHED_KEY, matchedKey)
|
||||||
|
core.info(
|
||||||
|
`uv cache restored from GitHub Actions cache with key: ${matchedKey}`
|
||||||
|
)
|
||||||
|
core.setOutput('cache-hit', true)
|
||||||
|
}
|
55
src/download/checksum/checksum.ts
Normal file
55
src/download/checksum/checksum.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import * as fs from 'fs'
|
||||||
|
import * as crypto from 'crypto'
|
||||||
|
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import {KNOWN_CHECKSUMS} from './known-checksums'
|
||||||
|
import {Architecture, Platform} from '../../utils/platforms'
|
||||||
|
|
||||||
|
export async function validateChecksum(
|
||||||
|
checkSum: string | undefined,
|
||||||
|
downloadPath: string,
|
||||||
|
arch: Architecture,
|
||||||
|
platform: Platform,
|
||||||
|
version: string
|
||||||
|
): Promise<void> {
|
||||||
|
let isValid = true
|
||||||
|
if (checkSum !== undefined && checkSum !== '') {
|
||||||
|
isValid = await validateFileCheckSum(downloadPath, checkSum)
|
||||||
|
} else {
|
||||||
|
core.debug(`Checksum not provided. Checking known checksums.`)
|
||||||
|
const key = `${arch}-${platform}-${version}`
|
||||||
|
if (key in KNOWN_CHECKSUMS) {
|
||||||
|
const knownChecksum = KNOWN_CHECKSUMS[`${arch}-${platform}-${version}`]
|
||||||
|
core.debug(`Checking checksum for ${arch}-${platform}-${version}.`)
|
||||||
|
isValid = await validateFileCheckSum(downloadPath, knownChecksum)
|
||||||
|
} else {
|
||||||
|
core.debug(`No known checksum found for ${key}.`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
throw new Error(`Checksum for ${downloadPath} did not match ${checkSum}.`)
|
||||||
|
}
|
||||||
|
core.debug(`Checksum for ${downloadPath} is valid.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function validateFileCheckSum(
|
||||||
|
filePath: string,
|
||||||
|
expected: string
|
||||||
|
): Promise<boolean> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const hash = crypto.createHash('sha256')
|
||||||
|
const stream = fs.createReadStream(filePath)
|
||||||
|
stream.on('error', err => reject(err))
|
||||||
|
stream.on('data', chunk => hash.update(chunk))
|
||||||
|
stream.on('end', () => {
|
||||||
|
const actual = hash.digest('hex')
|
||||||
|
resolve(actual === expected)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isknownVersion(version: string): boolean {
|
||||||
|
const pattern = new RegExp(`^.*-.*-${version}$`)
|
||||||
|
return Object.keys(KNOWN_CHECKSUMS).some(key => pattern.test(key))
|
||||||
|
}
|
3015
src/download/checksum/known-checksums.ts
Normal file
3015
src/download/checksum/known-checksums.ts
Normal file
File diff suppressed because it is too large
Load Diff
39
src/download/checksum/update-known-checksums.ts
Normal file
39
src/download/checksum/update-known-checksums.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import {promises as fs} from 'fs'
|
||||||
|
import * as tc from '@actions/tool-cache'
|
||||||
|
export async function updateChecksums(
|
||||||
|
filePath: string,
|
||||||
|
downloadUrls: string[]
|
||||||
|
): Promise<void> {
|
||||||
|
await fs.rm(filePath)
|
||||||
|
await fs.appendFile(
|
||||||
|
filePath,
|
||||||
|
'// AUTOGENERATED_DO_NOT_EDIT\nexport const KNOWN_CHECKSUMS: {[key: string]: string} = {\n'
|
||||||
|
)
|
||||||
|
let firstLine = true
|
||||||
|
for (const downloadUrl of downloadUrls) {
|
||||||
|
const content = await downloadAssetContent(downloadUrl)
|
||||||
|
const checksum = content.split(' ')[0].trim()
|
||||||
|
const key = getKey(downloadUrl)
|
||||||
|
if (!firstLine) {
|
||||||
|
await fs.appendFile(filePath, ',\n')
|
||||||
|
}
|
||||||
|
await fs.appendFile(filePath, ` '${key}':\n '${checksum}'`)
|
||||||
|
firstLine = false
|
||||||
|
}
|
||||||
|
await fs.appendFile(filePath, '}\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
function getKey(downloadUrl: string): string {
|
||||||
|
// https://github.com/astral-sh/uv/releases/download/0.3.2/uv-aarch64-apple-darwin.tar.gz.sha256
|
||||||
|
const parts = downloadUrl.split('/')
|
||||||
|
const fileName = parts[parts.length - 1]
|
||||||
|
const name = fileName.split('.')[0].split('uv-')[1]
|
||||||
|
const version = parts[parts.length - 2]
|
||||||
|
return `${name}-${version}`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadAssetContent(downloadUrl: string): Promise<string> {
|
||||||
|
const downloadPath = await tc.downloadTool(downloadUrl)
|
||||||
|
const content = await fs.readFile(downloadPath, 'utf8')
|
||||||
|
return content
|
||||||
|
}
|
64
src/download/download-latest.ts
Normal file
64
src/download/download-latest.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import * as core from '@actions/core'
|
||||||
|
import * as tc from '@actions/tool-cache'
|
||||||
|
import * as path from 'path'
|
||||||
|
import * as exec from '@actions/exec'
|
||||||
|
import {Architecture, Platform} from '../utils/platforms'
|
||||||
|
import {validateChecksum} from './checksum/checksum'
|
||||||
|
import {OWNER, REPO} from '../utils/utils'
|
||||||
|
|
||||||
|
export async function downloadLatest(
|
||||||
|
platform: Platform,
|
||||||
|
arch: Architecture,
|
||||||
|
checkSum: string | undefined,
|
||||||
|
githubToken: string | undefined
|
||||||
|
): Promise<{downloadDir: string; version: string}> {
|
||||||
|
const binary = `uv-${arch}-${platform}`
|
||||||
|
let downloadUrl = `https://github.com/${OWNER}/${REPO}/releases/latest/download/${binary}`
|
||||||
|
if (platform === 'pc-windows-msvc') {
|
||||||
|
downloadUrl += '.zip'
|
||||||
|
} else {
|
||||||
|
downloadUrl += '.tar.gz'
|
||||||
|
}
|
||||||
|
core.info(`Downloading uv from "${downloadUrl}" ...`)
|
||||||
|
|
||||||
|
const downloadDir = `${process.cwd()}${path.sep}uv`
|
||||||
|
const downloadPath = await tc.downloadTool(
|
||||||
|
downloadUrl,
|
||||||
|
downloadDir,
|
||||||
|
githubToken
|
||||||
|
)
|
||||||
|
let uvExecutablePath: string
|
||||||
|
let extracted: string
|
||||||
|
if (platform === 'pc-windows-msvc') {
|
||||||
|
extracted = await tc.extractZip(downloadPath)
|
||||||
|
uvExecutablePath = path.join(extracted, 'uv.exe')
|
||||||
|
} else {
|
||||||
|
extracted = await tc.extractTar(downloadPath)
|
||||||
|
uvExecutablePath = path.join(extracted, 'uv')
|
||||||
|
}
|
||||||
|
const version = await getVersion(uvExecutablePath)
|
||||||
|
await validateChecksum(checkSum, downloadPath, arch, platform, version)
|
||||||
|
|
||||||
|
return {downloadDir, version}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getVersion(uvExecutablePath: string): Promise<string> {
|
||||||
|
// Parse the output of `uv --version` to get the version
|
||||||
|
// The output looks like
|
||||||
|
// uv 0.3.1 (be17d132a 2024-08-21)
|
||||||
|
|
||||||
|
const options: exec.ExecOptions = {
|
||||||
|
silent: !core.isDebug()
|
||||||
|
}
|
||||||
|
const execArgs = ['--version']
|
||||||
|
|
||||||
|
let output = ''
|
||||||
|
options.listeners = {
|
||||||
|
stdout: (data: Buffer) => {
|
||||||
|
output += data.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await exec.exec(uvExecutablePath, execArgs, options)
|
||||||
|
const parts = output.split(' ')
|
||||||
|
return parts[1]
|
||||||
|
}
|
48
src/download/download-version.ts
Normal file
48
src/download/download-version.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import * as core from '@actions/core'
|
||||||
|
import * as tc from '@actions/tool-cache'
|
||||||
|
import {OWNER, REPO, TOOL_CACHE_NAME} from '../utils/utils'
|
||||||
|
import path from 'path'
|
||||||
|
import {Architecture, Platform} from '../utils/platforms'
|
||||||
|
import {validateChecksum} from './checksum/checksum'
|
||||||
|
|
||||||
|
export function tryGetFromToolCache(
|
||||||
|
arch: Architecture,
|
||||||
|
version: string
|
||||||
|
): string | undefined {
|
||||||
|
core.debug(`Trying to get uv from tool cache for ${version}...`)
|
||||||
|
const cachedVersions = tc.findAllVersions(TOOL_CACHE_NAME, arch)
|
||||||
|
core.debug(`Cached versions: ${cachedVersions}`)
|
||||||
|
return tc.find(TOOL_CACHE_NAME, version, arch)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downloadVersion(
|
||||||
|
platform: Platform,
|
||||||
|
arch: Architecture,
|
||||||
|
version: string,
|
||||||
|
checkSum: string | undefined,
|
||||||
|
githubToken: string | undefined
|
||||||
|
): Promise<string> {
|
||||||
|
const binary = `uv-${arch}-${platform}`
|
||||||
|
let downloadUrl = `https://github.com/${OWNER}/${REPO}/releases/download/${version}/${binary}`
|
||||||
|
if (platform === 'pc-windows-msvc') {
|
||||||
|
downloadUrl += '.zip'
|
||||||
|
} else {
|
||||||
|
downloadUrl += '.tar.gz'
|
||||||
|
}
|
||||||
|
core.info(`Downloading uv from "${downloadUrl}" ...`)
|
||||||
|
|
||||||
|
const downloadDir = `${process.cwd()}${path.sep}uv`
|
||||||
|
const downloadPath = await tc.downloadTool(
|
||||||
|
downloadUrl,
|
||||||
|
downloadDir,
|
||||||
|
githubToken
|
||||||
|
)
|
||||||
|
await validateChecksum(checkSum, downloadPath, arch, platform, version)
|
||||||
|
|
||||||
|
if (platform === 'pc-windows-msvc') {
|
||||||
|
await tc.extractZip(downloadPath)
|
||||||
|
} else {
|
||||||
|
tc.extractTar(downloadPath)
|
||||||
|
}
|
||||||
|
return downloadPath
|
||||||
|
}
|
36
src/save-cache.ts
Normal file
36
src/save-cache.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import * as cache from '@actions/cache'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
import {STATE_CACHE_MATCHED_KEY, STATE_CACHE_KEY} from './cache/restore-cache'
|
||||||
|
import {cacheLocalPath, enableCache} from './utils/inputs'
|
||||||
|
|
||||||
|
export async function run(): Promise<void> {
|
||||||
|
try {
|
||||||
|
if (enableCache) {
|
||||||
|
await saveCache()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const err = error as Error
|
||||||
|
core.setFailed(err.message)
|
||||||
|
}
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveCache(): Promise<void> {
|
||||||
|
const cacheKey = core.getState(STATE_CACHE_KEY)
|
||||||
|
const matchedKey = core.getState(STATE_CACHE_MATCHED_KEY)
|
||||||
|
|
||||||
|
if (!cacheKey) {
|
||||||
|
core.warning('Error retrieving cache key from state.')
|
||||||
|
return
|
||||||
|
} else if (matchedKey === cacheKey) {
|
||||||
|
// no change in target directories
|
||||||
|
core.info(`Cache hit occurred on key ${cacheKey}, not saving cache.`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
core.info(`Saving cache path: ${cacheLocalPath}`)
|
||||||
|
await cache.saveCache([cacheLocalPath], cacheKey)
|
||||||
|
|
||||||
|
core.info(`cache saved with the key: ${cacheKey}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
run()
|
98
src/setup-uv.ts
Normal file
98
src/setup-uv.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import * as core from '@actions/core'
|
||||||
|
import * as path from 'path'
|
||||||
|
import {downloadVersion, tryGetFromToolCache} from './download/download-version'
|
||||||
|
import {restoreCache} from './cache/restore-cache'
|
||||||
|
|
||||||
|
import {downloadLatest} from './download/download-latest'
|
||||||
|
import {Architecture, getArch, getPlatform, Platform} from './utils/platforms'
|
||||||
|
import {
|
||||||
|
cacheLocalPath,
|
||||||
|
checkSum,
|
||||||
|
enableCache,
|
||||||
|
githubToken,
|
||||||
|
version
|
||||||
|
} from './utils/inputs'
|
||||||
|
|
||||||
|
async function run(): Promise<void> {
|
||||||
|
const platform = getPlatform()
|
||||||
|
const arch = getArch()
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (platform === undefined) {
|
||||||
|
throw new Error(`Unsupported platform: ${process.platform}`)
|
||||||
|
}
|
||||||
|
if (arch === undefined) {
|
||||||
|
throw new Error(`Unsupported architecture: ${process.arch}`)
|
||||||
|
}
|
||||||
|
const installedVersion = await setupUv(
|
||||||
|
platform,
|
||||||
|
arch,
|
||||||
|
version,
|
||||||
|
checkSum,
|
||||||
|
githubToken
|
||||||
|
)
|
||||||
|
|
||||||
|
addMatchers()
|
||||||
|
setCacheDir(cacheLocalPath)
|
||||||
|
|
||||||
|
if (enableCache) {
|
||||||
|
await restoreCache(installedVersion)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
core.setFailed((err as Error).message)
|
||||||
|
}
|
||||||
|
process.exit(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setupUv(
|
||||||
|
platform: Platform,
|
||||||
|
arch: Architecture,
|
||||||
|
versionInput: string,
|
||||||
|
checkSum: string | undefined,
|
||||||
|
githubToken: string | undefined
|
||||||
|
): Promise<string> {
|
||||||
|
let installedPath: string | undefined
|
||||||
|
let downloadDir: string
|
||||||
|
let version: string
|
||||||
|
if (versionInput === 'latest') {
|
||||||
|
const result = await downloadLatest(platform, arch, checkSum, githubToken)
|
||||||
|
version = result.version
|
||||||
|
downloadDir = result.downloadDir
|
||||||
|
} else {
|
||||||
|
version = versionInput
|
||||||
|
installedPath = tryGetFromToolCache(arch, versionInput)
|
||||||
|
if (installedPath) {
|
||||||
|
core.info(`Found uv in tool-cache for ${versionInput}`)
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
downloadDir = await downloadVersion(
|
||||||
|
platform,
|
||||||
|
arch,
|
||||||
|
versionInput,
|
||||||
|
checkSum,
|
||||||
|
githubToken
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
addUvToPath(downloadDir)
|
||||||
|
core.setOutput('uv-version', version)
|
||||||
|
core.info(`Successfully installed uv version ${version}`)
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
|
||||||
|
function addUvToPath(cachedPath: string): void {
|
||||||
|
core.addPath(cachedPath)
|
||||||
|
core.info(`Added ${cachedPath} to the path`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCacheDir(cacheLocalPath: string): void {
|
||||||
|
core.exportVariable('UV_CACHE_DIR', cacheLocalPath)
|
||||||
|
core.info(`Set UV_CACHE_DIR to ${cacheLocalPath}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function addMatchers(): void {
|
||||||
|
const matchersPath = path.join(__dirname, `..${path.sep}..`, '.github')
|
||||||
|
core.info(`##[add-matcher]${path.join(matchersPath, 'python.json')}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
run()
|
65
src/update-default-version.ts
Normal file
65
src/update-default-version.ts
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import * as github from '@actions/github'
|
||||||
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
|
import {OWNER, REPO} from './utils/utils'
|
||||||
|
import {createReadStream, promises as fs} from 'fs'
|
||||||
|
import * as readline from 'readline'
|
||||||
|
import * as semver from 'semver'
|
||||||
|
|
||||||
|
import {updateChecksums} from './download/checksum/update-known-checksums'
|
||||||
|
|
||||||
|
async function run(): Promise<void> {
|
||||||
|
const checksumFilePath = process.argv.slice(2)[0]
|
||||||
|
const defaultVersionFilePath = process.argv.slice(2)[1]
|
||||||
|
const github_token = process.argv.slice(2)[2]
|
||||||
|
|
||||||
|
const octokit = github.getOctokit(github_token)
|
||||||
|
|
||||||
|
const response = await octokit.paginate(octokit.rest.repos.listReleases, {
|
||||||
|
owner: OWNER,
|
||||||
|
repo: REPO
|
||||||
|
})
|
||||||
|
const downloadUrls: string[] = response.flatMap(release =>
|
||||||
|
release.assets
|
||||||
|
.filter(asset => asset.name.endsWith('.sha256'))
|
||||||
|
.map(asset => asset.browser_download_url)
|
||||||
|
)
|
||||||
|
await updateChecksums(checksumFilePath, downloadUrls)
|
||||||
|
|
||||||
|
const latestVersion = response
|
||||||
|
.map(release => release.tag_name)
|
||||||
|
.sort(semver.rcompare)[0]
|
||||||
|
core.setOutput('latest-version', latestVersion)
|
||||||
|
await updateDefaultVersion(defaultVersionFilePath, latestVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateDefaultVersion(
|
||||||
|
filePath: string,
|
||||||
|
latestVersion: string
|
||||||
|
): Promise<void> {
|
||||||
|
const fileStream = createReadStream(filePath)
|
||||||
|
|
||||||
|
const rl = readline.createInterface({
|
||||||
|
input: fileStream
|
||||||
|
})
|
||||||
|
|
||||||
|
let foundDescription = false
|
||||||
|
const lines = []
|
||||||
|
|
||||||
|
for await (let line of rl) {
|
||||||
|
if (
|
||||||
|
!foundDescription &&
|
||||||
|
line.includes("description: 'The version of uv to install'")
|
||||||
|
) {
|
||||||
|
foundDescription = true
|
||||||
|
} else if (foundDescription && line.includes('default: ')) {
|
||||||
|
line = line.replace(/'[^']*'/, `'${latestVersion}'`)
|
||||||
|
foundDescription = false
|
||||||
|
}
|
||||||
|
lines.push(line)
|
||||||
|
}
|
||||||
|
|
||||||
|
await fs.writeFile(filePath, lines.join('\n'))
|
||||||
|
}
|
||||||
|
|
||||||
|
run()
|
9
src/utils/inputs.ts
Normal file
9
src/utils/inputs.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import * as core from '@actions/core'
|
||||||
|
|
||||||
|
export const version = core.getInput('version')
|
||||||
|
export const checkSum = core.getInput('checksum')
|
||||||
|
export const enableCache = core.getInput('enable-cache') === 'true'
|
||||||
|
export const cacheSuffix = core.getInput('cache-suffix') || ''
|
||||||
|
export const cacheLocalPath = core.getInput('cache-local-path')
|
||||||
|
export const githubToken = core.getInput('github-token')
|
||||||
|
export const cacheDependencyGlob = core.getInput('cache-dependency-glob')
|
33
src/utils/platforms.ts
Normal file
33
src/utils/platforms.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
export type Platform =
|
||||||
|
| 'unknown-linux-gnu'
|
||||||
|
| 'unknown-linux-musl'
|
||||||
|
| 'unknown-linux-musleabihf'
|
||||||
|
| 'apple-darwin'
|
||||||
|
| 'pc-windows-msvc'
|
||||||
|
export type Architecture = 'i686' | 'x86_64' | 'aarch64'
|
||||||
|
|
||||||
|
export function getArch(): Architecture | undefined {
|
||||||
|
const arch = process.arch
|
||||||
|
const archMapping: {[key: string]: Architecture} = {
|
||||||
|
ia32: 'i686',
|
||||||
|
x64: 'x86_64',
|
||||||
|
arm64: 'aarch64'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (arch in archMapping) {
|
||||||
|
return archMapping[arch]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlatform(): Platform | undefined {
|
||||||
|
const platform = process.platform
|
||||||
|
const platformMapping: {[key: string]: Platform} = {
|
||||||
|
linux: 'unknown-linux-gnu',
|
||||||
|
darwin: 'apple-darwin',
|
||||||
|
win32: 'pc-windows-msvc'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (platform in platformMapping) {
|
||||||
|
return platformMapping[platform]
|
||||||
|
}
|
||||||
|
}
|
3
src/utils/utils.ts
Normal file
3
src/utils/utils.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const REPO = 'uv'
|
||||||
|
export const OWNER = 'astral-sh'
|
||||||
|
export const TOOL_CACHE_NAME = 'uv'
|
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
|
||||||
|
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
|
||||||
|
"outDir": "./lib", /* Redirect output structure to the directory. */
|
||||||
|
"rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||||
|
"strict": true, /* Enable all strict type-checking options. */
|
||||||
|
"noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||||
|
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
|
},
|
||||||
|
"exclude": ["node_modules", "**/*.test.ts"]
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user