├── .cspell.json ├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── ci.yaml │ ├── codeql-analysis.yml │ └── pypi.yaml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── cosmofy.schema.json ├── examples ├── pkg-nested │ ├── __init__.py │ └── sub-folder │ │ ├── __main__.py │ │ └── ignore.py ├── pkg-with-init │ └── __init__.py ├── pkg-with-main │ ├── __main__.py │ ├── pkg.py │ └── py.typed └── single-file │ ├── file-no-docstring.py │ ├── file-no-main.py │ └── file-with-main.py ├── pyproject.toml ├── src └── cosmofy │ ├── __init__.py │ ├── __main__.py │ ├── args.py │ ├── bundler.py │ ├── downloader.py │ ├── py.typed │ ├── pythonoid.py │ ├── receipt.py │ ├── updater.py │ └── zipfile2.py ├── test ├── test_args.py ├── test_bundler.py ├── test_download.py ├── test_main.py ├── test_pythonoid.py ├── test_receipt.py ├── test_updater.py └── test_zipfile2.py └── uv.lock /.cspell.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2", 3 | "language": "en", 4 | "words": [ 5 | "Autobuild", 6 | "codeql", 7 | "compresslevel", 8 | "cosmofy", 9 | "exitmsg", 10 | "fpclose", 11 | "levelname", 12 | "libc", 13 | "metaist", 14 | "mypy", 15 | "myscript", 16 | "outl", 17 | "parsedate", 18 | "pdoc", 19 | "pycache", 20 | "pycs", 21 | "pypa", 22 | "pypi", 23 | "pyright", 24 | "pytest", 25 | "PYTHONINSPECT", 26 | "pythonoid", 27 | "setuptools", 28 | "venv", 29 | "zinfo" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 2 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.py] 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | CHANGELOG.md -text merge=union 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: metaist 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: { branches: [main] } 5 | pull_request: { branches: [main] } 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | strategy: 12 | matrix: 13 | python-version: ["3.9", "3.10", "3.11", "3.12"] 14 | 15 | steps: 16 | - name: Checkout repo 17 | uses: actions/checkout@v4 18 | 19 | - name: Set up Python ${{ matrix.python-version }} 20 | uses: actions/setup-python@v5 21 | with: 22 | python-version: ${{ matrix.python-version }} 23 | 24 | - name: Set up caches 25 | uses: actions/cache@v4 26 | with: 27 | path: ~/.cache/pip 28 | key: ${{ runner.os }}-py${{ matrix.python-version }} 29 | 30 | - name: Install dependencies 31 | run: | 32 | python -m pip install --upgrade pip setuptools wheel 33 | pip install -e ".[dev]" 34 | 35 | - name: Format & Lint (ruff, cspell) 36 | run: | 37 | ds lint 38 | 39 | - name: Type check (pyright, mypy) 40 | run: | 41 | ds types 42 | 43 | - name: Run tests (pytest, coverage) 44 | run: | 45 | ds test 46 | 47 | - name: Build docs (pdoc) 48 | run: | 49 | ds docs 50 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [main] 9 | 10 | jobs: 11 | analyze: 12 | name: Analyze 13 | runs-on: ubuntu-latest 14 | permissions: 15 | actions: read 16 | contents: read 17 | security-events: write 18 | 19 | strategy: 20 | fail-fast: false 21 | matrix: 22 | language: ["python"] 23 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] 24 | 25 | steps: 26 | - name: Checkout repository 27 | uses: actions/checkout@v4 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v3 31 | with: 32 | languages: ${{ matrix.language }} 33 | 34 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 35 | # If this step fails, then you should remove it and run the build manually (see below) 36 | - name: Autobuild 37 | uses: github/codeql-action/autobuild@v3 38 | 39 | - name: Perform CodeQL Analysis 40 | uses: github/codeql-action/analyze@v3 41 | -------------------------------------------------------------------------------- /.github/workflows/pypi.yaml: -------------------------------------------------------------------------------- 1 | name: Upload Python Package 2 | 3 | on: 4 | release: 5 | types: [published] 6 | 7 | jobs: 8 | pypi-publish: 9 | runs-on: ubuntu-latest 10 | 11 | permissions: 12 | id-token: write 13 | 14 | steps: 15 | - name: Checkout repo 16 | uses: actions/checkout@v4 17 | 18 | - name: Set up Python 19 | uses: actions/setup-python@v5 20 | with: 21 | python-version: "3.x" 22 | 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip setuptools wheel 26 | pip install build 27 | 28 | - name: Check install 29 | run: | 30 | pip install -e . 31 | 32 | - name: Build package 33 | run: | 34 | python -m build 35 | 36 | - name: Publish package 37 | uses: pypa/gh-action-pypi-publish@2f6f737ca5f74c637829c0f5c3acd0e29ea5e8bf 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | 3 | # general 4 | _ignore* 5 | _seed.py 6 | .venv* 7 | *.db 8 | 9 | # editors 10 | .idea 11 | .vim 12 | .vscode 13 | *.code-workspace 14 | .DS_Store 15 | 16 | # test 17 | .coverage 18 | .mypy_cache 19 | .pytest_cache 20 | .ruff_cache 21 | /htmlcov 22 | 23 | # build 24 | __pycache__ 25 | .pdm-python 26 | *.egg-info 27 | /build 28 | /dist 29 | node_modules/ 30 | 31 | # rye 32 | .python-version 33 | requirements-dev.lock 34 | requirements.lock 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog] and this project adheres to [Semantic Versioning]. 6 | 7 | Sections order is: `Fixed`, `Changed`, `Added`, `Deprecated`, `Removed`, `Security`. 8 | 9 | [keep a changelog]: http://keepachangelog.com/en/1.0.0/ 10 | [semantic versioning]: http://semver.org/spec/v2.0.0.html 11 | 12 | --- 13 | 14 | ## [Unreleased] 15 | 16 | [unreleased]: https://github.com/metaist/cosmofy/compare/prod...main 17 | 18 | These are changes that are on `main` that are not yet in `prod`. 19 | 20 | --- 21 | 22 | [#1]: https://github.com/metaist/cosmofy/issues/1 23 | [#2]: https://github.com/metaist/cosmofy/issues/2 24 | [#3]: https://github.com/metaist/cosmofy/issues/3 25 | [#4]: https://github.com/metaist/cosmofy/issues/4 26 | [#5]: https://github.com/metaist/cosmofy/issues/5 27 | [#6]: https://github.com/metaist/cosmofy/issues/6 28 | [#7]: https://github.com/metaist/cosmofy/issues/7 29 | [#8]: https://github.com/metaist/cosmofy/issues/8 30 | [#9]: https://github.com/metaist/cosmofy/issues/9 31 | [#10]: https://github.com/metaist/cosmofy/issues/10 32 | [#11]: https://github.com/metaist/cosmofy/issues/11 33 | [#12]: https://github.com/metaist/cosmofy/issues/12 34 | [#13]: https://github.com/metaist/cosmofy/issues/13 35 | [0.1.0]: https://github.com/metaist/cosmofy/commits/0.1.0 36 | 37 | ## [0.1.0] - 2024-09-18T18:55:19Z 38 | 39 | Initial release. 40 | 41 | **Added** 42 | 43 | - [#1], [#4], [#12]: bootstrap cosmofy to build itself 44 | - [#2], [#6], [#8], [#9], [#13]: JSON receipt and schema 45 | - [#3], [#5]: `--receipt-url`, `--release-url`, `--release-version` 46 | - [#7]: `--self-update`, `--self-update --help`, `--self-update --version` 47 | - [#10]: release notes 48 | - [#11]: auto-upload build artifacts to GitHub Release 49 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Toolchain 4 | 5 | The top-level tool chain for managing this project is tested on Linux and macOS. 6 | Here are links for installing the appropriate tools. 7 | 8 | - [`cspell`](https://cspell.org/docs/installation/) 9 | - [`ds`](https://github.com/metaist/ds#install) 10 | - [`gh`](https://github.com/cli/cli#installation) 11 | - [`git`](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) 12 | - [`uv`](https://github.com/astral-sh/uv#installation) 13 | 14 | All remaining tools are installed below. 15 | 16 | ## Local Development 17 | 18 | ```bash 19 | # get the code 20 | git clone git@github.com:metaist/cosmofy.git 21 | cd cosmofy 22 | uv sync --extra dev 23 | ``` 24 | 25 | Periodically, you should run: 26 | 27 | ```bash 28 | ds dev # check lint, type-checks, and run tests 29 | ``` 30 | 31 | This repo generally tries to maintain type-correctness (via `mypy` and `pyright`) and complete unit test coverage. 32 | 33 | ## Making a Release 34 | 35 | Checkout `prod`: 36 | 37 | ```bash 38 | git checkout prod 39 | git merge --no-ff --no-edit main 40 | ``` 41 | 42 | Update top-most `__init__.py`: 43 | 44 | ```python 45 | __version__ = "X.0.1" 46 | ``` 47 | 48 | Update `CHANGELOG.md`. To see recently closed issues run: 49 | 50 | ```bash 51 | ds recent-closed 52 | ``` 53 | 54 | You can also look at the [unreleased](https://github.com/metaist/cosmofy/compare/prod...main) log too. 55 | 56 | Sections order is: `Fixed`, `Changed`, `Added`, `Deprecated`, `Removed`, `Security`. 57 | 58 | ```markdown 59 | --- 60 | 61 | [X.0.1]: https://github.com/metaist/cosmofy/compare/X.0.0...X.0.1 62 | 63 | ## [X.0.1] - XXXX-XX-XXT00:00:00Z 64 | 65 | **Fixed** 66 | 67 | **Changed** 68 | 69 | **Added** 70 | 71 | **Deprecated** 72 | 73 | **Removed** 74 | 75 | **Security** 76 | ``` 77 | 78 | ### Final checks, tag, and push 79 | 80 | ```bash 81 | export VER="X.0.1" 82 | 83 | # final checks again every supported python version 84 | ds dev-all # requires uv >= 0.3.0 85 | 86 | # final build 87 | ds docs build 88 | 89 | # commit, push tags, create a new release 90 | ds release: $VER 91 | ``` 92 | 93 | [Review the release on GitHub](https://github.com/metaist/cosmofy/releases). Once published, the `pypi.yaml` workflow will attempt to publish it to PyPI. 94 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright 2025 Metaist LLC. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cosmofy: Cosmopolitan Python Bundler 2 | 3 |
8 | 9 | `cosmofy` bundles your python app into a **single executable** which runs on 10 | Linux, macOS, and Windows. It uses [Cosmopolitan libc](https://github.com/jart/cosmopolitan). 11 | 12 | ## Install 13 | 14 | macOS / Linux: 15 | 16 | ```bash 17 | dest=~/.local/bin/cosmofy 18 | curl -sSz $dest -o $dest -L https://github.com/metaist/cosmofy/releases/latest/download/cosmofy 19 | chmod +x $dest 20 | ``` 21 | 22 | Windows: (PowerShell instructions coming soon) 23 | 24 | ## Examples 25 | 26 | ```bash 27 | # bundle a script with python 28 | cosmofy myscript.py # produces `myscript.com` 29 | ./myscript.com # runs on macOS / Linux / Windows 30 | 31 | # bundle a whole directory; change output path 32 | cosmofy src/my-pkg --args '-m my-pkg --more-args' --output dist/my-pkg 33 | ./dist/my-pkg # starts bundled python with "-m my-pky --more-args" 34 | 35 | # bundle self-updater (see below) 36 | cosmofy src/my-pkg \ 37 | --release-url https://github.com/metaist/cosmofy/releases/latest/download/cosmofy 38 | ./my-pkg.com # run app as normal 39 | ./my-pkg.com --self-update # run cosmofy.updater to install any updates 40 | ``` 41 | 42 | ## Usage 43 | 44 | 48 | 49 | ```text 50 | cosmofy: Cosmopolitan Python Bundler 51 | 52 | USAGE 53 | 54 | cosmofy 55 | [--help] [--version] [--debug] [--dry-run] [--self-update] 56 | [--python-url URL] [--cache PATH] [--clone] 57 | [--output PATH] [--args STRING] 58 |