Skip to content

Commit

Permalink
🔧 Add UV cache functionality for improved performance (#72)
Browse files Browse the repository at this point in the history
  • Loading branch information
yezz123 authored Sep 24, 2024
2 parents 598b73d + 9250b86 commit 920b652
Show file tree
Hide file tree
Showing 10 changed files with 71,896 additions and 25,476 deletions.
53 changes: 33 additions & 20 deletions .github/workflows/default.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
name: default
name: CI

on:
pull_request:
push:
branches:
- main
pull_request: {}

jobs:
# run linters and unit tests
lint-and-test-units:
runs-on: ubuntu-latest
steps:
Expand All @@ -20,25 +22,15 @@ jobs:
- run: npm run format:check
- run: npm run test

# run action on a clean machine without building to check that it works as expected
test-integration:
runs-on: ${{ matrix.os }}

strategy:
matrix:
python-version:
- "3.8"
- "3.9"
- "3.10"
- "3.11"
- "3.12"
uv-version:
- "0.1.12"
- ""
os:
- macos-latest
- ubuntu-latest
- windows-latest
python-version: ["3.10"]
uv-version: ["0.3.0", ""]
os: [macos-latest, ubuntu-latest, windows-latest]
cache: [true, false]
venv-name: [".venv"]
fail-fast: true

steps:
Expand All @@ -49,7 +41,8 @@ jobs:
- uses: ./
with:
uv-version: ${{ matrix.uv-version }}
uv-venv: "my_virt_env"
uv-venv: ${{ matrix.venv-name }}
uv-cache: ${{ matrix.cache }}
- run: uv --version
- name: Get python executable on Windows
if: runner.os == 'Windows'
Expand All @@ -60,4 +53,24 @@ jobs:
run: |
which python
- name: Check inside virtual environment
run: python -c "import sys; exit(0 if sys.prefix != sys.base_prefix else 1)"
run: python -c "import sys; exit(0 if sys.prefix != sys.base_prefix else 1)"
- name: init Project
run: uv init
- name: Install Pydantic and Ruff
run: |
uv add pydantic ruff
- name: Check installed packages
run: |
uv pip list
- name: Check cache directory (non-Windows)
if: runner.os != 'Windows' && matrix.cache == 'true'
run: |
ls -la ${{ env.UV_CACHE_DIR || '/tmp/.uv-cache' }}
echo "Checking for Pydantic and Ruff in cache"
find ${{ env.UV_CACHE_DIR || '/tmp/.uv-cache' }} -name "*pydantic*" -o -name "*ruff*"
- name: Check cache directory (Windows)
if: runner.os == 'Windows' && matrix.cache == 'true'
run: |
Get-ChildItem -Force ${{ env.UV_CACHE_DIR || '$env:TEMP\.uv-cache' }}
echo "Checking for Pydantic and Ruff in cache"
Get-ChildItem -Recurse ${{ env.UV_CACHE_DIR || '$env:TEMP\.uv-cache' }} | Where-Object { $_.Name -like "*pydantic*" -or $_.Name -like "*ruff*" }
142 changes: 130 additions & 12 deletions __tests__/input.test.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,173 @@
import { getInputs, getVenvInput, getVersionInput } from '../src/inputs'
import {
getInputs,
getVenvInput,
getVersionInput,
getCacheInput,
isCacheAllowed
} from '../src/inputs'
import * as core from '@actions/core'

jest.mock('@actions/core')

const mockedCore = core as jest.Mocked<typeof core>

const TEST_ENV_VARS = {
INPUT_MISSING: '',
INPUT_FALSY: 'false',
INPUT_TRUTHY: 'true',
INPUT_VERSION_UNSUPPORTED: '0.0.3',
INPUT_VERSION_SUPPORTED: '0.1.2',

INPUT_VERSION_CACHE_SUPPORTED: '0.3.0',
INPUT_VERSION_LATEST: 'latest',
'INPUT_UV-VERSION': '0.1.2',
'INPUT_UV-VENV': 'my_venv'
'INPUT_UV-VENV': 'my_venv',
'INPUT_UV-CACHE': 'true'
}

describe('options', () => {
beforeEach(() => {
jest.resetAllMocks()
for (const key in TEST_ENV_VARS) {
process.env[key] = TEST_ENV_VARS[key as keyof typeof TEST_ENV_VARS]
}
})

afterEach(() => {
for (const key in TEST_ENV_VARS) {
Reflect.deleteProperty(TEST_ENV_VARS, key)
delete process.env[key]
}
})

it('getVersionInput returns null if input is missing', () => {
mockedCore.getInput.mockReturnValue('')
expect(getVersionInput('missing')).toBeNull()
expect(mockedCore.notice).toHaveBeenCalledWith('Using latest uv version')
})

it('getInputs returns inputs', () => {
expect(getInputs()).toStrictEqual({
version: '0.1.2',
venv: 'my_venv'
})
it('getVersionInput returns null if input is "latest"', () => {
mockedCore.getInput.mockReturnValue('latest')
expect(getVersionInput('version_latest')).toBeNull()
expect(mockedCore.notice).toHaveBeenCalledWith('Using latest uv version')
})

it('getVersionInput throws if input is not valid', () => {
mockedCore.getInput.mockReturnValue('false')
expect(() => getVersionInput('falsy')).toThrow(
"Passed uv version 'false' is not a valid"
"Passed uv version 'false' is not valid"
)
})

it('getVersionInput returns version if input is supported', () => {
expect(getVersionInput('version_supported')).toBe('0.1.2')
it('getVersionInput warns if version is unsupported', () => {
mockedCore.getInput.mockReturnValue('0.2.9')
expect(getVersionInput('version_unsupported')).toBe('0.2.9')
expect(mockedCore.warning).toHaveBeenCalledWith(
"Passed uv version '0.2.9' is less than 0.3.0. Caching will be disabled."
)
expect(mockedCore.warning).toHaveBeenCalledWith(
'Using uv version 0.2.9. This may not be the latest version.'
)
})

it('getVersionInput warns for supported version', () => {
mockedCore.getInput.mockReturnValue('0.3.0')
expect(getVersionInput('version_supported')).toBe('0.3.0')
expect(mockedCore.warning).toHaveBeenCalledWith(
'Using uv version 0.3.0. This may not be the latest version.'
)
})

it('getVenvInput returns venv name if input is valid', () => {
mockedCore.getInput.mockReturnValue('my_venv')
expect(getVenvInput('uv-venv')).toBe('my_venv')
})

it('getVenvInput returns null if input is not provided', () => {
mockedCore.getInput.mockReturnValue('')
expect(getVenvInput('SOMETHING')).toBeNull()
})

it('getInputs returns inputs including cache', () => {
mockedCore.getInput.mockImplementation(name => {
if (name === 'uv-version') {
return '0.3.0'
}
if (name === 'uv-venv') {
return 'my_venv'
}
if (name === 'uv-cache') {
return 'true'
}
return ''
})
expect(getInputs()).toStrictEqual({
version: '0.3.0',
venv: 'my_venv',
cache: true
})
expect(mockedCore.warning).toHaveBeenCalledWith(
'Using uv version 0.3.0. This may not be the latest version.'
)
})

it('getCacheInput returns true if input is true and version is supported', () => {
mockedCore.getInput.mockReturnValue('true')
expect(getCacheInput('uv-cache', '0.3.0')).toBe(true)
})

it('getCacheInput returns false if input is true but version is not supported', () => {
mockedCore.getInput.mockReturnValue('true')
expect(getCacheInput('uv-cache', '0.2.9')).toBe(false)
expect(mockedCore.warning).toHaveBeenCalledWith(
'Cache requested but uv version is less than 0.3.0. Caching will be disabled.'
)
})

it('getCacheInput returns false if input is false', () => {
mockedCore.getInput.mockReturnValue('false')
expect(getCacheInput('uv-cache', '0.3.0')).toBe(false)
})

it('getCacheInput returns true if input is true and version is null (latest)', () => {
mockedCore.getInput.mockReturnValue('true')
expect(getCacheInput('uv-cache', null)).toBe(true)
})

it('isCacheAllowed returns true for supported versions', () => {
expect(isCacheAllowed('0.3.0')).toBe(true)
expect(isCacheAllowed('0.4.0')).toBe(true)
expect(isCacheAllowed(null)).toBe(true) // null represents latest version
})

it('isCacheAllowed returns false for unsupported versions', () => {
expect(isCacheAllowed('0.2.9')).toBe(false)
expect(isCacheAllowed('0.1.0')).toBe(false)
})

it('getCacheInput returns false if cache is not requested, even if version is supported', () => {
mockedCore.getInput.mockReturnValue('false')
expect(getCacheInput('uv-cache', '0.3.0')).toBe(false)
})

it('getInputs returns cache as false when cache is not requested, even if version is supported', () => {
mockedCore.getInput.mockImplementation(name => {
if (name === 'uv-version') {
return '0.3.0'
}
if (name === 'uv-venv') {
return 'my_venv'
}
if (name === 'uv-cache') {
return 'false'
}
return ''
})
expect(getInputs()).toStrictEqual({
version: '0.3.0',
venv: 'my_venv',
cache: false
})
expect(mockedCore.warning).toHaveBeenCalledWith(
'Using uv version 0.3.0. This may not be the latest version.'
)
})
})
14 changes: 12 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
name: Setup uv
description: Set up your GitHub Actions workflow with a specific version of uv
author: Yasser Tahiri

inputs:
uv-version:
description: uv version to use, if version is not provided then latest stable version will be used
description: uv version to use. If not provided, the latest stable version will be used.
required: false
uv-venv:
description: virtual environment name to create and activate.
description: Virtual environment name to create and activate.
required: false
uv-cache:
description: Whether to cache uv packages. Only supported for uv versions >= 0.3.0.
required: false
default: 'false'
uv-cache-dir:
description: Directory to use for uv cache. If not specified, a platform-appropriate default will be used.
required: false

runs:
using: node20
main: dist/index.js

branding:
icon: package
color: purple
Loading

0 comments on commit 920b652

Please sign in to comment.