https://github.com/metaist/cosmofy
Cosmopolitan Python Bundler
https://github.com/metaist/cosmofy
Last synced: 6 months ago
JSON representation
Cosmopolitan Python Bundler
- Host: GitHub
- URL: https://github.com/metaist/cosmofy
- Owner: metaist
- License: mit
- Created: 2024-09-10T12:20:14.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2025-01-15T02:20:14.000Z (over 1 year ago)
- Last Synced: 2025-04-26T01:25:50.097Z (about 1 year ago)
- Language: Python
- Size: 141 KB
- Stars: 23
- Watchers: 2
- Forks: 0
- Open Issues: 6
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- Funding: .github/FUNDING.yml
- License: LICENSE.md
Awesome Lists containing this project
- awesome-python-standalone - GitHub - 2% open · ⏱️ 13.01.2026): (Freezers)
- awesome-cosmopolitan - cosmofy - Bundle Python projects into a single-file Cosmopolitan Python executable (Cosmopolitan / Projects)
README
# cosmofy: Cosmopolitan Python Bundler
`cosmofy` bundles your python app using [`uv`](https://docs.astral.sh/uv/) into a **single executable** which runs on
Linux, macOS, and Windows using [Cosmopolitan libc](https://github.com/jart/cosmopolitan).
## Install
First, make sure you have [`uv` installed](https://docs.astral.sh/uv/getting-started/installation/).
To use the latest version of `cosmofy` without installing, use `uvx`:
```bash
uvx cosmofy bundle
```
To install `cosmofy` locally:
```bash
uv tool install cosmofy # recommended
# or
pip install cosmofy # classic
```
You can also install the `cosmofy` binary directly.
macOS / Linux:
```bash
dest=~/.local/bin/cosmofy
curl -sSz $dest -o $dest -L https://github.com/metaist/cosmofy/releases/latest/download/cosmofy
chmod +x $dest
```
Windows:
```powershell
$dest = "$env:LOCALAPPDATA\cosmofy\cosmofy.exe"
New-Item -ItemType Directory -Force -Path (Split-Path $dest)
Invoke-WebRequest -Uri "https://github.com/metaist/cosmofy/releases/latest/download/cosmofy" -OutFile $dest
```
## Examples
To bundle a project, first make sure you define `[project.scripts]` in your `pyproject.toml`:
```toml
[project.scripts]
my_command = "my_command.__main__:main"
```
The bundle the whole project:
```bash
uvx cosmofy bundle # produces `dist/my_command`
```
You can also bundle individual python files:
```bash
uv init --script myscript.py # or uv add --script myscript.py
uvx cosmofy bundle --script myscript.py # produces `dist/myscript`
```
## Limitations
- Currently, we can only bundle pure-Python code (no C extensions) (see [#94])
- Currently, we're tied to the latest Cosmopolitan Python version (see [#44]).
- Automatically compiling Python bytecode is currently disabled (see [#41]).
[#44]: https://github.com/metaist/cosmofy/issues/44
[#41]: https://github.com/metaist/cosmofy/issues/41
[#94]: https://github.com/metaist/cosmofy/issues/94
## Self Updater
> [!WARNING]
> This is an experimental feature. See [Security Considerations](#security-considerations).
There is experimental support for [adding a self-updater](#cosmofy-updater-add) to a cosmofy bundle:
```bash
uvx cosmofy updater add dist/my_cmd # produces dist/mc_cmd.json
```
This produces a `.json` receipt that you should publish together with your bundle.
- If the bundle is run with `--self-update` anywhere in the arguments,
the cosmofy updater will run. It will compare it's internal build
date with the date at `--receipt-url` and will download any updates, if
they exist.
- Otherwise, the bundle will run as normal by calling `.args`.
[See below](#supported-python-cli) for minor limitations.
## Security Considerations
The self-updater verifies downloaded binaries against the hash in the receipt,
but does not cryptographically verify the receipt itself. If you control the
receipt hosting, this provides integrity verification. For higher-assurance
scenarios, receipt signing is planned for a future release (see [#53]).
[#53]: https://github.com/metaist/cosmofy/issues/53
## Supported Python CLI
Cosmopolitan Python apps have a special `.args` file which is read when it
starts up. The contents of this file are typically set during
[`cosmofy bundle`](#cosmofy-bundle) and can be adjusted with
[`cosmofy fs args`](#cosmofy-fs-args).
However, when using the [self-updater](#self-updater), we need to check for
the `--self-update` option first.
If `--self-update` is NOT present, we want to process the rest of the
`.args` as usual. However, since Python has already started, we only support
the following [Python Command Line Interface options](https://docs.python.org/3/using/cmdline.html):
- `-c `: run a command
- `-m `: run a module (this is the most common)
- `-`: read a command from `stdin` (rare, but we support it)
- ``: run a script on the filesystem
- `-V, --version`: display the Python version (we also support `-VV`)
- `-h, -?, --help`: show relevant portions of the help message
- `-i`: enter python REPL after executing a script (`-c`, `-m`, `-`, or `<script>`)
- `-q`: don't display copyright and version messages in interactive mode
If no option is provided, the Python REPL will run.
## Commands
- [`cosmofy bundle`](#cosmofy-bundle)
- [`cosmofy updater`](#cosmofy-updater)
- [`cosmofy updater add`](#cosmofy-updater-add)
- [`cosmofy updater remove`](#cosmofy-updater-remove)
- [`cosmofy updater check`](#cosmofy-updater-check)
- [`cosmofy fs`](#cosmofy-fs)
- [`cosmofy fs ls`](#cosmofy-fs-ls)
- [`cosmofy fs cat`](#cosmofy-fs-cat)
- [`cosmofy fs add`](#cosmofy-fs-add)
- [`cosmofy fs rm`](#cosmofy-fs-rm)
- [`cosmofy fs args`](#cosmofy-fs-args)
- [`cosmofy self`](#cosmofy-self)
- [`cosmofy self update`](#cosmofy-self-update)
- [`cosmofy self version`](#cosmofy-self-version)
### `cosmofy`
<!--[[[cog
from cosmofy.__main__ import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
A Cosmopolitan Python bundler.
Usage: cosmofy [OPTIONS] <COMMAND>
Commands:
bundle build and bundle a project
updater install/uninstall bundle self-updater
fs inspect and modify an existing bundle
self manage the `cosmofy` executable
Options:
--version display the program version and exit
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy bundle`
<!--[[[cog
from cosmofy.bundle import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
Build a Python project into a Cosmopolitan bundle.
Usage: cosmofy bundle [OPTIONS]
Input options:
--entry <NAME> `console_script` entry points to bundle
--script <PATH> paths to bundle
If neither --entry nor --script is specified, all entry points
will be bundled. If both are specified, entries will be bundled first.
--python-url <URL> URL from which to download Cosmopolitan Python
[default: https://cosmo.zip/pub/cosmos/bin/python]
[env: COSMOFY_PYTHON_URL=]
Output options:
-o, --output-dir <PATH> output directory
[default: project-root/dist]
Cache options:
-n, --no-cache do not read or save to the cache
[env: COSMOFY_NO_CACHE=]
--cache-dir <PATH> store Cosmopolitan Python downloads
[default: ~/.cache/cosmofy]
[env: COSMOFY_CACHE_DIR=]
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy updater`
<!--[[[cog
from cosmofy.updater.__main__ import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
EXPERIMENTAL: Manage a bundle's self-updater.
Usage: cosmofy updater [OPTIONS] <COMMAND>
Commands:
add add self-updater to a bundle
remove remove self-updater from a bundle
check check if the bundle has updates
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy updater add`
<!--[[[cog
from cosmofy.updater.add import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
EXPERIMENTAL: Add self-updater to a cosmofy bundle.
Usage: cosmofy updater add <BUNDLE> [OPTIONS]
Arguments:
<BUNDLE> Cosmopolitan file bundle
Receipt options:
--receipt <PATH> output path to the JSON receipt
default is <BUNDLE> + `.json`
--receipt-url <URL> URL to the published receipt
default is --release-url + `.json`
[env: RECEIPT_URL=]
--release-url <URL> URL to the file to download
default is --receipt-url without `.json`
[env: RELEASE_URL=]
--release-version <STRING>
release version
default is $(<BUNDLE> --version)
Process options:
--no-copy skip copying `cosmofy` code
(e.g., its already a dependency)
[env: COSMOFY_NO_COPY=]
--no-args skip setting `.args`
[env: COSMOFY_NO_ARGS=]
read more: https://github.com/metaist/cosmofy#self-updater
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy updater remove`
<!--[[[cog
from cosmofy.updater.remove import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
EXPERIMENTAL: Remove cosmofy self-updater from a cosmofy bundle.
Usage: cosmofy updater remove <BUNDLE> [OPTIONS]
Arguments:
<BUNDLE> Cosmopolitan file bundle
Process options:
--no-args skip setting `.args`
[env: COSMOFY_NO_ARGS=]
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy updater check`
<!--[[[cog
from cosmofy.updater.check import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
EXPERIMENTAL: Check if a cosmofy bundle has an update.
Usage: cosmofy updater check <BUNDLE> [OPTIONS]
Arguments:
<BUNDLE> Cosmopolitan file bundle
Options:
--receipt-url <URL> override the published receipt URL
[env: RECEIPT_URL=]
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy fs`
<!--[[[cog
from cosmofy.fs.__main__ import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
Cosmopolitan file system tool.
Usage: cosmofy fs [OPTIONS] <COMMAND>
Commands:
ls list files in bundle
cat print file contents
add add files to bundle
rm remove files from bundle
args get/set special `.args` file in bundle
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy fs ls`
<!--[[[cog
from cosmofy.fs.ls import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
List contents of a Cosmopolitan bundle.
Usage: cosmofy fs ls <BUNDLE> [OPTIONS] [FILE]...
Arguments:
<BUNDLE> Cosmopolitan file bundle
[FILE]... one or more file patterns to show
Filter options:
-a, --all show entries whose name starts with `.`
-B, --ignore-backups hide entries whose name ends with `~`
--hide <PATTERN> hide matching entries, unless --all
-I, --ignore <PATTERN> hide matching entries, even with --all
Sort options:
-r, --reverse reverse the sort order
--sort <MODE> [choices: none, name, size, time, extension]
[default: name]
Output options:
-l, --long show permissions, size, and modified date
-h, --human-readable show sizes using powers of 1024 like 1K 2M 3G etc.
--si show sizes using powers of 1000 (implies -h)
Global options:
--help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy fs cat`
<!--[[[cog
from cosmofy.fs.cat import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
Print contents of a file within a Cosmopolitan bundle.
Usage: cosmofy fs cat <BUNDLE> <FILE>... [OPTIONS]
Arguments:
<BUNDLE> Cosmopolitan file bundle
<FILE>... one or more file patterns to show
tip: Use `--` to separate options from filenames that start with `-`
Example: cosmofy fs cat bundle.zip -- -weird-filename.txt
Options:
-p, --prompt prompt for a decryption password
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy fs add`
<!--[[[cog
from cosmofy.fs.add import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
Add files to a Cosmopolitan bundle.
Usage: cosmofy fs add <BUNDLE> [OPTIONS] <FILE>...
Arguments:
<BUNDLE> Cosmopolitan file bundle
<FILE>... files relative to current directory to add
tip: Use `--` to separate options from filenames that start with `-`
Example: cosmofy fs add bundle.zip -- -weird-filename.txt
Options:
-f, --force overwrite existing files
--chdir <PATH> change to this directory before adding
--dest prefix to add in the bundle
Most python packages go into `Lib/site-packages`
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy fs rm`
<!--[[[cog
from cosmofy.fs.rm import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
Remove files from a Cosmopolitan bundle.
Usage: cosmofy fs rm <BUNDLE> [OPTIONS] <FILE>...
Arguments:
<BUNDLE> Cosmopolitan file bundle
<FILE>... files to remove
tip: Use `--` to separate options from filenames that start with `-`
Example: cosmofy fs rm bundle.zip -- -weird-filename.txt
Options:
-f, --force ignore nonexistent files
-r, --recursive recursively remove directories
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy fs args`
<!--[[[cog
from cosmofy.fs.args import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
Get or set the special `.args` files in a Cosmopolitan bundle.
These are the arguments to the Cosmopolitan Python.
Usage: cosmofy fs args <BUNDLE> [OPTIONS] [VAL]
Arguments:
<BUNDLE> Cosmopolitan file bundle
[VAL] value to set (if omitted, current value is printed)
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy self`
<!--[[[cog
from cosmofy.self.__main__ import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
Manage the `cosmofy` executable.
Usage: cosmofy self [OPTIONS] <COMMAND>
Commands:
update update `cosmofy`
version display `cosmofy`'s version
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy self update`
<!--[[[cog
from cosmofy.self.update import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
Update `cosmofy`.
Usage: cosmofy self update [OPTIONS]
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
### `cosmofy self version`
<!--[[[cog
from cosmofy.self.version import usage
cog.outl(f"\n```text\n{usage}```\n")
]]]-->
```text
Display `cosmofy`'s version.
Usage: cosmofy self version [OPTIONS]
Options:
--short only print the version
--output-format NAME [default: text][choices: text, json]
Global options:
-h, --help show this help message
-q, --quiet... show quiet output
-v, --verbose... show verbose output
--dry-run do not make any filesystem changes
--color <COLOR> control output color
[default: auto][choices: auto, always, never]
(auto checks `NO_COLOR`, `FORCE_COLOR`, `CLICOLOR`,
`CLICOLOR_FORCE` and tty support)
```
<!--[[[end]]]-->
## License
[MIT License](https://github.com/metaist/cosmofy/blob/main/LICENSE.md)