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:
-
Non-portable shebang: The recommended shebang uses
#!/usr/bin/env -S uv run --script, which relies on the non-standard-Sflag that doesn't work on some systems (uv issue #11876) -
No executable bit:
uv init --scriptcreates files that aren't executable by default, requiring a manualchmod +x -
Inconsistent syncing: After
uv add --scriptoruv 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 runuv sync --script -
No reproducibility by default: Scripts don't include
exclude-newertimestamps, 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-newertimestamps
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:
- Updates the shebang to
#!/usr/bin/env uvrs - Adds PEP 723 metadata with an
exclude-newertimestamp (if not present) - Runs
uv sync --script --upgradeto 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:
- Updates the
exclude-newerfield in the script's[tool.uv]section to the current UTC timestamp - Runs
uv sync --script --upgradeto 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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1e996f7119899f50717b949e1d3735f2acf75d489a4cc6eb51d7221cf44e9d69
|
|
| MD5 |
0585f3afadb324799646e14b948b2215
|
|
| BLAKE2b-256 |
aeaf3b10fa4f899cb9c94ec72429d741f23f232dec61db61e2222afa4b4a93bf
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
761276c2bae3a50b436761eeb03d62b8a8f86e0b2010fb786b89cb958f8512c6
|
|
| MD5 |
73e034c81b5fdc536296145055a9e8a6
|
|
| BLAKE2b-256 |
ef04dd70507b62d1227d00ae5e73ba819ab78978843ad2084328ef359a7405cb
|