Skip to main content

Create and run uv scripts with POSIX standardized shebang line

Project description

uvrs

A tool for managing uv scripts more easily.

This is uv run --script, uv add --script, and uv remove --script rolled into one, with calls to uv sync --script automatically made to minimize unhappy surprises with the automatically-managed virtual environment.

Unlike uv, uvrs adds a shebang line, sets an executable bit, and uses timestamp-pins requirements by default.

Why this exists

While uv has excellent support for inline script metadata (PEP 723), there are several rough edges when managing scripts:

  1. Non-portable shebang: The recommended shebang uses #!/usr/bin/env -S uv run --script, which relies on the non-standard -S flag that doesn't work on some systems (uv issue #11876)

  2. No executable bit: uv init --script creates files that aren't executable by default, requiring a manual chmod +x

  3. Inconsistent syncing: After uv add --script or uv remove --script, the virtual environment isn't synced - running the script may implicitly sync new packages, but removed packages always stay installed until you manually run uv sync --script

  4. No reproducibility by default: Scripts don't include exclude-newer timestamps, so running the same script weeks later may use different package versions, breaking reproducibility

uvrs addresses all of these issues:

  • Portable shebang that works everywhere: #!/usr/bin/env uvrs
  • Executable by default for new and fixed scripts
  • Automatic syncing after add/remove operations
  • Reproducible by default with automatic exclude-newer timestamps

Installation

This should be installed as a globally available tool (so the above shebang line works):

uv tool install -p 3.14 uvrs

That will install uvrs using Python 3.14 (for nicely colorized help text).

What each command does

Here's what uvrs does under the hood, compared to the equivalent uv workflow:

uvrs init <path>

This is equivalent to:

uv init --script <path>
# Add #!/usr/bin/env uvrs shebang
# Add exclude-newer timestamp to metadata
chmod +x <path>

uvrs fix <path>

This is equivalent to:

# Update shebang to #!/usr/bin/env uvrs
# Add exclude-newer timestamp if missing
uv sync --script <path> --upgrade
chmod +x <path>

uvrs add <path> <package>

This is equivalent to:

uv add --script <path> <package>
uv sync --script <path>

uvrs remove <path> <package>

This is equivalent to:

uv remove --script <path> <package>
uv sync --script <path>

uvrs stamp <path>

This is equivalent to:

# Update exclude-newer timestamp to current time
uv sync --script <path> --upgrade

Creating new uv scripts

To initialize a new uv script with a uvrs shebang line use the init command:

uvrs init ~/bin/my-script --python 3.12

This will create the file ~/bin/my-script using uv init --script ~/bin/my-script --python 3.12 and then add an appropriate shebang line to the beginning of the script.

By default, uvrs init also adds an exclude-newer timestamp to improve reproducibility:

#!/usr/bin/env uvrs
# /// script
# requires-python = ">=3.12"
# dependencies = []
#
# [tool.uv]
# exclude-newer = "2025-10-15T20:30:45Z"
# ///


def main() -> None:
    print("Hello from my-script!")


if __name__ == "__main__":
    main()

To skip adding the timestamp, use --no-stamp:

uvrs init ~/bin/my-script --no-stamp

Updating existing scripts

To update an existing Python script to use the uvrs shebang, use the fix command:

uvrs fix ~/bin/my-script

This command:

  1. Updates the shebang to #!/usr/bin/env uvrs
  2. Adds PEP 723 metadata with an exclude-newer timestamp (if not present)
  3. Runs uv sync --script --upgrade to ensure the environment is up to date

For example, a plain Python script like:

#!/usr/bin/env python
print("Hello!")

Will be transformed to:

#!/usr/bin/env uvrs
# /// script
# dependencies = []
#
# [tool.uv]
# exclude-newer = "2025-10-16T00:25:00Z"
# ///

print("Hello!")

To skip adding the timestamp and metadata, use --no-stamp:

uvrs fix ~/bin/my-script --no-stamp

Managing dependencies

To update the dependencies within inline script metadata, use uvrs add and uvrs remove.

To add a new dependency:

uvrs add ~/bin/my-script 'rich'

This runs uv add --script ~/bin/my-script 'rich' followed by uv sync --script ~/bin/my-script, so the dependency is installed immediately.

To remove a new dependency:

uvrs remove ~/bin/my-script 'rich'

This runs uv remove --script ~/bin/my-script 'rich' and then uv sync --script ~/bin/my-script to keep the resolved environment up to date.

Updating timestamps and upgrading dependencies

To update the exclude-newer timestamp and upgrade all dependencies to the latest versions allowed by your constraints, use the stamp command:

uvrs stamp ~/bin/my-script

This command:

  1. Updates the exclude-newer field in the script's [tool.uv] section to the current UTC timestamp
  2. Runs uv sync --script --upgrade to upgrade dependencies and rebuild the environment

The exclude-newer field limits package versions to those published before the specified timestamp, which improves reproducibility by preventing unexpected updates.

The goal

Eventually, I would like to see a similar tool integrated into uv.

Until that time, I plan to maintain this uvrs tool.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

uvrs-0.7.0.tar.gz (7.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

uvrs-0.7.0-py3-none-any.whl (8.0 kB view details)

Uploaded Python 3

File details

Details for the file uvrs-0.7.0.tar.gz.

File metadata

  • Download URL: uvrs-0.7.0.tar.gz
  • Upload date:
  • Size: 7.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.2

File hashes

Hashes for uvrs-0.7.0.tar.gz
Algorithm Hash digest
SHA256 1e996f7119899f50717b949e1d3735f2acf75d489a4cc6eb51d7221cf44e9d69
MD5 0585f3afadb324799646e14b948b2215
BLAKE2b-256 aeaf3b10fa4f899cb9c94ec72429d741f23f232dec61db61e2222afa4b4a93bf

See more details on using hashes here.

File details

Details for the file uvrs-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: uvrs-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 8.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.9.2

File hashes

Hashes for uvrs-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 761276c2bae3a50b436761eeb03d62b8a8f86e0b2010fb786b89cb958f8512c6
MD5 73e034c81b5fdc536296145055a9e8a6
BLAKE2b-256 ef04dd70507b62d1227d00ae5e73ba819ab78978843ad2084328ef359a7405cb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page