Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
name: CD

run-name: CD - ${{ github.event_name == 'workflow_run' && format('{0} (from CI)', github.event.workflow_run.head_commit.message) || format('{0}{1}', github.event.inputs.environment != '' && github.event.inputs.environment || 'dev', github.event.inputs.version != '' && format(' (v{0})', github.event.inputs.version) || '') }}

on:
workflow_dispatch:
inputs:
version:
description: "Release version"
default: "None"
required: false
workflow_run:
workflows: ["CI"]
branches: [main]
types:
- completed

env:
CD: ${{ vars.CONTINUOUS_DEPLOYMENT }}
DEPLOY_MODE: "true"
VERSION: ${{ github.event.inputs.version != '' && github.event.inputs.version || 'None' }}

jobs:
# Continuous Deployment (CD) pipeline
cd:
if: ${{ vars.CONTINUOUS_DEPLOYMENT == 'true' && github.ref_name == 'main' && (github.event_name == 'workflow_dispatch' || (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')) }}
permissions:
contents: write
id-token: write
timeout-minutes: 15
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
platforms: [linux/amd64]
steps:
- name: Checkout Git repository
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Install uv and set the Python version
uses: astral-sh/setup-uv@v7

- name: Install dependencies
shell: bash
run: uvx uvtask dev-install

- name: Set version
shell: bash
run: |
if [ -z "${VERSION}" ] || [ "${VERSION}" = "None" ]; then
uv version --bump minor
VERSION=$(uv version --short)
echo "VERSION=${VERSION}" >> "${GITHUB_ENV:-/dev/null}"
else
uv version ${VERSION}
fi
git config user.name "${{ github.actor }}"
git config user.email "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com"
git tag -a "${VERSION}" -m "Release ${VERSION}"
git push origin "${VERSION}"
if: ${{ env.PIPELINE_TAG == 'true' && env.RELEASE_MODE == 'true' }}

- name: Build package
shell: bash
run: uv build
if: ${{ env.PIPELINE_TAG == 'true' && env.RELEASE_MODE == 'true' }}

- name: Upload package to artifact registry
uses: actions/upload-artifact@v6
with:
name: uvtask
path: dist/
if: ${{ env.PIPELINE_TAG == 'true' && env.RELEASE_MODE == 'true' }}

- name: Publish package
shell: bash
run: uv publish
if: ${{ env.PIPELINE_TAG == 'true' && env.RELEASE_MODE == 'true' }}

- name: Clean
shell: bash
run: uvx uvtask clean
80 changes: 80 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: CI

run-name: CI - ${{ github.event_name == 'pull_request' && github.event.pull_request.title || github.event_name == 'push' && github.event.head_commit.message || github.ref_name }} ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' && format('(v{0})', github.event.inputs.version) || '' }}

on:
push:
branches: [main]
pull_request:
workflow_dispatch:
inputs:
version:
description: "Release version"
default: "None"
required: false

env:
CI: "true"

jobs:
# Continuous Integration (CI) pipeline
ci:
if: ${{ vars.CONTINUOUS_INTEGRATION == 'true' }}
permissions:
contents: read
env:
PIPELINE_TESTS: ${{ github.event_name != 'workflow_dispatch' && github.event.inputs.version == '' && startsWith(github.ref, 'refs/tags/') == false && github.ref != 'refs/heads/main' && 'true' || 'false' }}
RELEASE_MODE: "false"
VERSION: ${{ github.run_id }}
timeout-minutes: 15
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
platforms: [linux/amd64, linux/arm64]
steps:
- name: Checkout Git repository
uses: actions/checkout@v6
with:
fetch-depth: 0

- name: Install uv and set the Python version
uses: astral-sh/setup-uv@v7

- name: Install dependencies
shell: bash
run: uvx uvtask dev-install

- name: security-analysis-licenses
shell: bash
run: uvx uvtask security-analysis:licenses
if: ${{ env.PIPELINE_TESTS == 'true' }}

- name: security-analysis-vulnerabilities
shell: bash
run: uvx uvtask security-analysis:vulnerabilities
if: ${{ env.PIPELINE_TESTS == 'true' }}

- name: static-analysis-linter
shell: bash
run: uvx uvtask static-analysis:linter
if: ${{ env.PIPELINE_TESTS == 'true' }}

- name: static-analysis-types
shell: bash
run: uvx uvtask static-analysis:types
if: ${{ env.PIPELINE_TESTS == 'true' }}

- name: unit-tests
shell: bash
run: uvx uvtask unit-tests
if: ${{ env.PIPELINE_TESTS == 'true' }}

- name: integration-tests
shell: bash
run: uvx uvtask integration-tests
if: ${{ env.PIPELINE_TESTS == 'true' }}

- name: Clean
shell: bash
run: uvx uvtask clean
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
*.egg-info/
*.pyc
__pycache__/
.coverage
build/
dist/
requirements-dev.txt
Expand Down
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.14
55 changes: 38 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,46 +1,67 @@
# πŸš€ uvtask
# uvtask

[![PyPI version](https://badge.fury.io/py/uvtask.svg)](https://badge.fury.io/py/uvtask)
[![image](https://img.shields.io/pypi/v/uvtask.svg)](https://pypi.python.org/pypi/uvtask)
[![image](https://img.shields.io/pypi/l/uvtask.svg)](https://pypi.python.org/pypi/uvtask)
[![image](https://img.shields.io/pypi/pyversions/uvtask.svg)](https://pypi.python.org/pypi/uvtask)
[![Actions status](https://github.com/aiopy/python-uvtask/actions/workflows/ci.yml/badge.svg)](https://github.com/aiopy/python-uvtask/actions)
[![PyPIDownloads](https://static.pepy.tech/badge/uvtask)](https://pepy.tech/project/uvtask)

**uvtask** is a modern, fast, and flexible Python task runner and test automation tool designed to simplify development workflows. It supports running, organizing, and managing tasks or tests in Python projects with an emphasis on ease of use and speed. ⚑
An extremely fast Python task runner.

## Highlights

- ⚑ **Extremely fast** - Built for speed with zero installation overhead
- πŸ“ **Simple configuration** - Define scripts in `pyproject.toml`
- πŸ”— **Pre/post hooks** - Automatically run hooks before and after commands
- 🎨 **Beautiful output** - Colorful, `uv`-inspired CLI

## 🎯 Quick Start

Run tasks defined in your `pyproject.toml`:
Run `uvtask` directly with `uvx` (no installation required):

```shell
uvx uvtask <task_name>
uvx uvtask <OPTIONS> [COMMAND]
```

Or install it and use it directly:

```shell
uv add --dev uvtask
uvtask <OPTIONS> [COMMAND]
```

## πŸ“ Configuration

Define your tasks in `pyproject.toml` under the `[tool.run-script]` section:
Define your scripts in `pyproject.toml` under the `[tool.run-script]` section:

```toml
[tool.run-script]
hello-world = "echo 'hello world'"
install = "uv sync --dev --all-extras"
format = "ruff format ."
lint = { command = "ruff check .", description = "Check code quality" }
check = ["ty check .", "mypy ."]
pre-test = "echo 'Running tests...'"
test = "pytest"
post-test = "echo 'Tests completed!'"
deploy = [
"echo 'Building...'",
"uv build",
"echo 'Deploying...'",
"uv deploy"
]
```

## πŸ› οΈ Development

To run the development version:

```shell
uvx --no-cache --from $PWD run --help
uvx -q --no-cache --from $PWD uvtask
```

## πŸ“‹ Requirements

- 🐍 Python >= 3.13

## 🀝 Contributing

Contributions are welcome! πŸŽ‰

- For major changes, please open an issue first to discuss what you would like to change
- Make sure to update tests as appropriate
- Follow the existing code style and conventions
Contributions are welcome! Please feel free to submit a Pull Request.

## πŸ“„ License

Expand Down
43 changes: 18 additions & 25 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
[build-system]
build-backend = "setuptools.build_meta"
requires = ["setuptools"]
requires = ["uv_build"]
build-backend = "uv_build"

[tool.uv.build-backend]
module-name = "uvtask"
module-root = ""

[project]
authors = [
Expand All @@ -27,8 +31,8 @@ classifiers = [
"Programming Language :: Python :: 3.14",
]
dependencies = []
description = "uvtask is a modern, fast, and flexible Python task runner and test automation tool designed to simplify development workflows. It supports running, organizing, and managing tasks or tests in Python projects with an emphasis on ease of use and speed."
dynamic = ["version"]
description = "An extremely fast Python task runner."
version = "0.0.0"
keywords = [
"uv",
"uvx",
Expand All @@ -51,25 +55,13 @@ dev = [
"pytest-cov>=7.0.0", # test, coverage
"pytest-xdist>=3.8.0", # test
"ruff>=0.14.10", # code-formatter, static-analysis
"ty>=0.0.4", # static-analysis
"ty>=0.0.6", # static-analysis
]

[project.urls]
"documentation" = "https://aiopy.github.io/python-uvtask/"
"repository" = "https://github.com/aiopy/python-uvtask"

[tool.setuptools.dynamic]
version = { attr = "uvtask.__version__" }

[tool.setuptools.packages.find]
include = ["uvtask*"]

[tool.setuptools.package-data]
"uvtask" = ["py.typed"]

[[tool.uv.index]]
url = "https://pypi.org/simple"

[tool.bandit]
exclude_dirs = ["tests"]
skips = ["B404", "B602"]
Expand Down Expand Up @@ -103,6 +95,8 @@ lint.select = [
]
lint.ignore = [
"PLC0415",
"PLR0912",
"PLR0913",
"PLR2004",
]

Expand All @@ -118,7 +112,7 @@ force-single-line = false
known-first-party = ["uvtask"]

[tool.ruff.lint.mccabe]
max-complexity = 15
max-complexity = 20

[tool.ty.environment]
python-version = "3.13"
Expand All @@ -127,35 +121,34 @@ python-version = "3.13"
exclude = [
"tests/fixtures/**",
"var",
".venv",
]

[tool.run-script]
install = "uv sync --frozen --no-dev"
upgrade-install = "uv sync --frozen --no-dev --upgrade --refresh"
dev-install = "uv sync --dev --all-extras"
upgrade-dev-install = "uv sync --dev --all-extras --upgrade --refresh"
deploy = "uv build && uv publish"
docs = "python3 -m mkdocs build -f docs_src/config/en/mkdocs.yml && python3 -m mkdocs build -f docs_src/config/es/mkdocs.yml"
dev-docs = "python3 -m mkdocs serve -f docs_src/config/en/mkdocs.yml"
code-formatter = "uv run ruff format uvtask tests $@"
code-formatter = "uv run ruff format uvtask tests"
"security-analysis:licenses" = "uv run pip-licenses"
"security-analysis:vulnerabilities" = "uv run bandit -r -c pyproject.toml uvtask tests"
"static-analysis:linter" = "uv run ruff check uvtask tests"
"static-analysis:types" = "uv run ty check uvtask tests"
test = "uv run pytest"
unit-tests = "uv run pytest tests/unit"
integration-tests = "uv run pytest tests/integration"
functional-tests = "uv run pytest -n1 tests/functional"
coverage = "uv run pytest -n1 --cov --cov-report=html"
clean = """python3 -c \"
clean = """python3 -c "
from glob import iglob
from shutil import rmtree

for pathname in ['./build', './*.egg-info', './dist', './var', '**/__pycache__']:
for path in iglob(pathname, recursive=True):
rmtree(path, ignore_errors=True)
\""""
"
"""

[project.scripts]
uvtask = "uvtask.cli:main"
run = "uvtask.cli:main"
run-script = "uvtask.cli:main"
2 changes: 1 addition & 1 deletion tests/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"""Test package for uvtask."""

Loading