Metadata-Version: 2.4
Name: compilecode
Version: 0.0.0
Summary: Compile a Python project to native modules with Nuitka; no key, no runtime decryption.
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: nuitka>=4.3.1

# compilecode

`compilecode` compiles a project's Python modules to native `.so` extension
modules with [Nuitka](https://nuitka.net). The original `.py` sources and
bytecode are removed afterwards. There is no key and no runtime decryption:
the compiled modules are imported directly by Python.

## Usage

Drop-in one-liner, run from the project root:

```bash
python -m compilecode .
```

Or, after installing the package (`pip install -e .`):

```bash
compilecode path/to/project
```

Options:

- `--strip-docstrings` removes docstrings from the compiled modules.

The exit code is `0` on success and `1` if any module failed to compile or
its source was not fully removed.

## Drop-in swap (Docker)

The reference `references/Dockerfile` keeps the same `compileit.sh` one-liner
against `$DOCKERHOME`, so existing setups swap in without other changes. The line now
compiles the project to `.so` modules with Nuitka instead of encrypting it.
No key is generated or shipped in the image, so there is nothing to store or
rotate. The builder stage installs `nuitka`, and the runtime stage no longer
installs `git`, `vim` or `dos2unix`.

## Ignoring files

Place a `.compilecodeignore` file in the project root to override which files
are compiled. It holds one pattern per line; blank lines and lines starting
with `#` are ignored. A pattern matches files that are left as plain Python.
A line starting with `!` forces a file to be compiled even if another rule
would skip it.

## Packaging

`compileit.sh` ships inside the installed `compilecode/` package. It runs
`python -m compilecode` and honours the `PYTHON` environment variable to pick
the interpreter.

## Releasing to PyPI / TestPyPI

`release.sh` versions, builds, and publishes the package. Everything runs
**inside the docker compose `test` container** — you only need Docker on the
host; `build`, `twine`, and `setuptools_scm` are never installed locally.

The version lives in **git tags** (via `setuptools_scm`): you never edit a
version by hand. `release.sh` computes the next one, tags the commit, builds
the sdist + wheel, uploads, and pushes the commit and tag back.

```bash
./release.sh <testpypi|pypi> [version] [options]
```

**Version selection** (pick at most one; default is a patch bump):

| You pass            | Version used                 | Example         |
| ------------------- | ---------------------------- | --------------- |
| *(nothing)*         | patch bump of the latest tag | `0.1.3 → 0.1.4` |
| *(nothing)*, but HEAD is already on a tag | that tag is **reused** (so `testpypi` → `pypi` publishes the same version) | `0.1.3` |
| `--minor`           | minor bump, patch reset      | `0.1.3 → 0.2.0` |
| `--major`           | major bump, minor+patch reset | `0.1.3 → 1.0.0` |
| `--version X.Y.Z`   | exactly that                 | `2.5.1`         |

**Options**

- `--dry-run` — compute the version, build, and `twine check` only. Makes
  **no git changes**, uploads nothing, pushes nothing. Run this first.
- `--no-push` — publish but skip `git push --follow-tags`.

**Authentication** (only needed for a real upload). Either works:

1. A `.pypirc` with `[pypi]` / `[testpypi]` sections (the default when no
   token env is set). It is searched as `$PYPIRC`, then `./.pypirc`, then
   `~/.pypirc`, mounted read-only into the container, and used via
   `twine upload --repository <target>`. The `[testpypi]` section must include
   its `repository = https://test.pypi.org/legacy/` line:

   ```ini
   [pypi]
     username = __token__
     password = pypi-AgEI...
   [testpypi]
     repository = https://test.pypi.org/legacy/
     username = __token__
     password = pypi-AgEI...
   ```

2. `export TWINE_API_TOKEN="pypi-...."` — a single token that takes precedence
   over `.pypirc` (matches twine's own env-over-file rule). Handy for CI.

Create tokens at <https://pypi.org/manage/account/token/> (or test.pypi.org).

**First release**

```bash
./release.sh testpypi --version 0.1.0 --dry-run   # rehearse, no side effects
./release.sh testpypi --version 0.1.0             # tag v0.1.0, publish to TestPyPI
./release.sh pypi     --version 0.1.0             # same version → real PyPI
```

**Routine release** after merging new commits:

```bash
./release.sh testpypi --dry-run    # shows the next patch, e.g. 0.1.1
./release.sh testpypi              # tag v0.1.1, publish to TestPyPI, push
./release.sh pypi                  # HEAD on v0.1.1 → reuse → publish to PyPI
```

Verify a TestPyPI upload installs (its deps still come from real PyPI):

```bash
pip install --index-url https://test.pypi.org/simple/ \
    --extra-index-url https://pypi.org/simple/ compilecode
```

## Try it end-to-end (smoke test)

`references/examples/sample-project/` is a small app that exercises the tricky
cases (nested relative imports, a dataclass, a data file, an entry script). Its
`smoke.sh` builds the real two-stage Docker image — compile under full
`python:3.13`, run under `python:3.13-slim` — and asserts the compiled app
runs while its `.py` sources are gone:

```bash
./references/examples/sample-project/smoke.sh
```
