Commands Reference¶
Complete reference for all appenv commands.
Global Options¶
./appenv --help
update-lockfile¶
Update the dependency lockfile (uv.lock). See Locking Behavior for details on UV’s locking model.
./appenv update-lockfile # Update lockfile
./appenv update-lockfile --diff # Show changes without writing
Options¶
Option |
Description |
|---|---|
|
Show full diff without writing lockfile |
Use APPENV_VERBOSE=1 for verbose output.
When to Use¶
After changing dependencies in
pyproject.toml(add, remove, or update version constraints)Before deploying — ensures the lockfile reflects current requirements
When onboarding — run once after cloning to generate
uv.lock
See Common Workflows for detailed examples.
init¶
Create a new pyproject.toml project. Interactive by default; use flags for scripting.
Interactive Mode (default)¶
Run without flags — init asks five questions:
Dependencies (one per line, empty line to finish; default:
app)Binary to expose — creates
./<name>symlink that runs the installed<name>binary (default:app)Project name (default:
<directory name>)Description
Minimum Python version (default:
3.13)
$ ./appenv init
Let's create a new appenv project in myproject
I'll ask a few questions, then create pyproject.toml here
Enter dependencies (one per line, empty line to finish):
Default: app
Dependency: httpie
Binary to expose (creates ./<name> symlink) [app] http
The default binary name is always app. To expose a different binary (e.g. http), type it at the prompt or create a symlink: ln -s appenv http.
Non-Interactive Mode (scripting / CI)¶
Pass --binary to skip all prompts. --dep is required (at least one) — init only creates new projects and refuses to touch an existing [project] section.
appenv init --binary http --dep httpie --dep pytest --name myproject
Without a TTY and missing required flags, init prints an error with a usage example and exits with code 64 (USAGE).
Options¶
Flag |
Description |
|---|---|
|
Target directory for the new project (default: current directory) |
|
Binary to expose as |
|
Package dependency. Repeat for multiple: |
|
Project name (default: directory name). |
|
Minimum Python version, format |
|
Project description (default: empty). Non-interactive only. |
Additional Symlinks¶
The symlink name must match a binary installed by your dependencies. Create additional symlinks to expose more binaries:
# After installing ruff and pytest as dependencies
ln -s appenv ruff
ln -s appenv pytest
./ruff check .
./pytest -xvs
Existing Project: Refused¶
If pyproject.toml already has a [project] section, init exits with code 65 (DATAERR) and prints guidance pointing at ./appenv uv add and direct editing. This prevents silent field loss: earlier versions rewrote the whole [project] section, dropping fields appenv cannot round-trip without a TOML writer (readme, license, classifiers, urls, dynamic, scripts, gui-scripts, [project.scripts], [project.optional-dependencies]).
$ ./appenv init
pyproject.toml already has a [project] section.
appenv `init` no longer updates existing projects (silent field loss risk).
Use `./appenv uv add` to manage dependencies, or edit pyproject.toml directly.
This behavior is identical on every supported Python version (3.9+).
If appenv Script Is Outdated¶
When the local ./appenv script has a different version than the running appenv, init prints a warning:
Warning: ./appenv is version 0.0.1, running appenv is 2026.3.19.
Run './appenv self-update' to update the script.
migrate¶
Convert an existing requirements.txt into pyproject.toml. For appenv projects that still use requirements.txt instead of pyproject.toml.
./appenv migrate
Options¶
path— Target directory (default: current directory)
What It Does¶
Reads
requirements.txtfrom the current directoryCreates or updates
pyproject.tomlwith those dependenciesGenerates
uv.lockautomaticallySeparates pip-options (
--index-url,--extra-index-url,--hash, …) from dependencies instead of writing them as broken specifiers — see Handling pip-optionsSkips editable installs (
-e) with a warningCleans up old
.appenv/artifactsUpdates the local
./appenvscript if the running version differs from the one on diskCreates or updates
.gitignorewith.venv,.appenv, and.batou-lockentries
Handling pip-options¶
Every line starting with - is treated as a pip-option and kept out of the [project] dependencies list. This is what lets migration work for requirements.txt files that previously crashed uv lock. Each category of option is handled differently:
Index URLs → [[tool.uv.index]]¶
--index-url and --extra-index-url are translated into [[tool.uv.index]] entries in pyproject.toml. This is what unblocks projects using private registries (GitLab Package Registry, AWS CodeArtifact, Artifactory).
Credentials are never copied into pyproject.toml. If an index URL carries embedded credentials (deploy token, API key), migrate strips them and writes only the clean URL, then tells you which environment variables to set so uv lock can authenticate. The names follow uv’s index authentication convention:
Variable |
Purpose |
|---|---|
|
Username for index |
|
Password or token for index |
<NAME> is the index name in uppercase. Set these in your shell (or CI secrets) before running uv lock. As an alternative to environment variables, you can put credentials in ~/.netrc.
Unsupported options → dropped with warning¶
These pip-options have no pyproject.toml equivalent and are dropped:
--hash,--require-hashes--no-binary--only-binary
migrate lists each skipped option so nothing disappears silently. If you depend on hash-pinning, audit the generated uv.lock yourself — uv computes its own hashes.
Editable installs¶
-e lines are skipped with a warning. Add them to pyproject.toml manually if you still need them.
Running via uvx¶
If appenv is not yet in your project, you can run migrate directly:
uvx appenv migrate
If appenv is not yet on stable PyPI:
uvx --prerelease allow appenv migrate
This downloads the latest appenv into your project and runs the migration. Run the command from the project directory that contains requirements.txt.
After Migrating¶
Test it (./appenv run <binary> --help — or create a symlink with ln -s appenv <binary> first), then remove requirements.txt.
See Common Workflows for a full migration walkthrough.
Failure Cases¶
No
requirements.txtfound — exits normally with a suggestion to useinitpyproject.tomlalready has[project]section — exits normally without changes
self-update¶
Update the local ./appenv script to match the currently running version.
./appenv self-update
Options¶
Option |
Description |
|---|---|
|
Check for version drift without updating (exit 0 if up-to-date, exit 1 if drift detected) |
|
Target directory containing the appenv script (default: project directory) |
What It Does¶
Compares the
__version__in the local./appenvscript with the currently running versionIf versions differ: replaces the script with the running version
If versions match: reports that the script is already up-to-date
With
--check: only reports drift status, does not modify any files
Running via uvx¶
When running via uvx, appenv runs from an externally managed environment and cannot update itself in place. Specify the target directory:
uvx appenv self-update .
uvx appenv self-update /path/to/project
If appenv is not yet on stable PyPI:
uvx --prerelease allow appenv self-update .
–check Mode¶
Use --check in CI or scripts to detect version drift:
./appenv self-update --check
Exit codes:
0 — script is up-to-date
1 — version drift detected
Failure Cases¶
No
./appenvscript found — prints error, exits with code 67 (NOINPUT)
prepare¶
Create the virtual environment with production dependencies. Requires an existing uv.lock — run update-lockfile first.
./appenv prepare
What It Does¶
Validates that
pyproject.tomlanduv.lockexistCreates
.appenv/venvwithuv venvInstalls production dependencies with
uv sync --no-dev --frozenUpdates
.venvsymlink to point to.appenv/venv
When to Use¶
CI/CD pipelines that need the venv before running commands
Debugging: recreate the venv without removing and rebuilding from scratch
Deployment scripts that prepare the environment explicitly
Symlink Dispatch Alternative¶
Running ./http (symlink to appenv) auto-prepares on first use. Explicit prepare is only needed when you want to control the timing.
reset¶
Remove the virtual environment and clean up legacy artifacts.
./appenv reset
What It Removes¶
.venvsymlink.appenv/venvdirectoryOld hash-based venvs in
.appenv/
What It Preserves¶
Logs in
.appenv/logs/Cached uv binary in
.appenv/.uv/Profiling data in
.appenv/profiling/Legacy symlink to current venv at
.appenv/currentpyproject.tomlanduv.lockSource code and other project files
Example Usage¶
# After experiencing issues with the virtual environment
$ ./appenv reset
Removing .venv symlink ...
Removing .appenv/venv ...
# Then recreate it
$ ./appenv prepare
version¶
Show appenv version.
./appenv version
./appenv --version
python¶
Start a Python REPL in the virtual environment.
./appenv python # Start REPL
./appenv python -c "print('hello')" # Execute code
./appenv python script.py --verbose # Run script with args
Details¶
Automatically ensures the virtual environment is prepared (equivalent to running
preparefirst)Production dependencies only — dev dependencies are excluded
For
python -m pytestor other dev tool usage, useuv runinstead:uv run python -m pytest
Example¶
$ ./appenv python
Python 3.x.x ...
Type "help", "copyright", "credits" or "license" for more information.
>>>
run¶
Run a command in the project virtual environment. Equivalent to uv run with appenv’s configured paths.
./appenv run pytest -xvs
./appenv run ruff check .
./appenv run python -c "print('hello')"
All arguments are passed through to uv run unchanged. This is useful for CI or deployment scripts that need to run arbitrary commands in the venv.
Tip: For commands you use frequently, create a symlink instead of typing appenv run every time:
ln -s appenv pytest
ln -s appenv ruff
./pytest -xvs # equivalent to: appenv run pytest -xvs
See the init command for details on symlink setup.
uv¶
Pass-through to the uv binary with appenv’s configured environment (Python interpreter, venv paths). Use this when you need uv functionality not covered by dedicated appenv commands.
./appenv uv add requests # Add production dependency
./appenv uv add --group dev pytest # Add dev dependency
./appenv uv sync # Re-sync dependencies
./appenv uv lock --upgrade # Upgrade all packages
All arguments are passed through to uv unchanged. See Common Workflows for dependency management examples.
Environment Variables¶
Variable |
Description |
|---|---|
|
Show verbose output (uv commands, Python selection) |
|
Extras to install (comma-separated) |
|
Base directory of the project (auto-set) |
|
Selected Python interpreter (auto-set) |
See Common Workflows for verbose mode example.
Exit Codes¶
appenv uses BSD sysexits.h exit codes:
Code |
Name |
Description |
|---|---|---|
64 |
USAGE |
Incorrect command usage — unrecognized arguments or self-update from externally managed environment |
65 |
DATAERR |
Input data issue (e.g., invalid pyproject.toml) |
67 |
NOINPUT |
Missing input file (e.g., no pyproject.toml found) |
68 |
UNAVAILABLE |
Resource unavailable (e.g., required tool not found) |
passthrough |
run / uv / python |
These commands delegate to the underlying tool; that tool’s exit code propagates unchanged. |
1 |
(self-update –check only) |
Version drift detected by |
Working with Extras¶
# pyproject.toml
[project.optional-dependencies]
dev = ["pytest", "ruff"]
APPENV_EXTRAS=dev ./http