Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 10 additions & 47 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
name: "CodeQL"

on:
push:
branches: [master]
pull_request:
# The branches below must be a subset of the branches above
branches: [master]
schedule:
- cron: '0 1 * * 4'
Expand All @@ -23,50 +17,19 @@ jobs:
strategy:
fail-fast: false
matrix:
# Override automatic language detection by changing the below list
# Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python']
language: ['go']
# Learn more...
# https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection

steps:
- name: Checkout repository
uses: actions/checkout@v2
with:
# We must fetch at least the immediate parents so that if this is
# a pull request then we can checkout the head.
fetch-depth: 2
- name: Checkout repository
uses: actions/checkout@v6

# If this run was triggered by a pull request event, then checkout
# the head of the pull request instead of the merge commit.
- run: git checkout HEAD^2
if: ${{ github.event_name == 'pull_request' }}
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
- name: Autobuild
uses: github/codeql-action/autobuild@v4

# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1

# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl

# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language

#- run: |
# make bootstrap
# make release

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
54 changes: 54 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Test

on:
push:
branches: [master]
pull_request:
branches: [master]

jobs:
test:
name: E2E (${{ matrix.pm }})
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
pm: [uv, poetry, pipenv, pip]

steps:
- uses: actions/checkout@v6

- name: Set up Go
uses: actions/setup-go@v6
with:
go-version-file: go.mod
cache: true

- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"

- name: Install uv
if: matrix.pm == 'uv'
uses: astral-sh/setup-uv@v8.0.0

- name: Install Poetry
if: matrix.pm == 'poetry'
uses: snok/install-poetry@v1
with:
virtualenvs-in-project: true

- name: Install pipenv
if: matrix.pm == 'pipenv'
run: pip install pipenv

- name: go build
run: go build ./...

- name: go vet
run: go vet ./...

- name: go test
run: go test -v -timeout 120s ./python/ -run TestE2E
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
.venv/
__pypackages__/
.sisyphus/
105 changes: 105 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# AGENTS.md

This file documents the go-python-runner project for AI coding agents.

## Project Overview

`go-python-runner` is a Go library that runs Python scripts in isolated virtual environments. It detects which Python package manager a project uses, creates the virtual environment, and provides the correct Python binary path to execute scripts.

Module path: `github.com/zoispag/go-python-runner`

## Repository Structure

```
go-python-runner/
├── python/ # Core library package
│ ├── virtualenv.go # Public API: SetupVirtualEnv, CleanUpVirtualEnv
│ ├── run.go # Public API: GetPythonRunCommand, ExecutePython
│ ├── pep518.go # Routes pyproject.toml projects to uv or Poetry
│ ├── uv.go # uv handler: runs `uv sync`
│ ├── poetry.go # Poetry handler: runs `poetry install`
│ ├── pipenv.go # pipenv handler: runs `pipenv install`
│ ├── pip.go # pip+venv handler: creates venv and pip installs
│ ├── utility.go # fileExists() and dirExists() helpers
│ └── e2e_test.go # E2E tests (one per package manager)
├── testdata/ # E2E test fixtures (one subdir per package manager)
│ ├── uv/ # uv fixture: pyproject.toml, uv.lock, script.py
│ ├── poetry/ # Poetry fixture: pyproject.toml, poetry.lock, poetry.toml, script.py
│ ├── pipenv/ # pipenv fixture: Pipfile, Pipfile.lock, script.py
│ └── pip/ # pip fixture: requirements.txt, script.py
├── .github/workflows/
│ ├── codeql-analysis.yml
│ └── test.yml # E2E CI: matrix over uv/poetry/pipenv/pip
├── go.mod
└── go.sum
```

## Public API

All public functions are in the `python` package (`github.com/zoispag/go-python-runner/python`).

### `SetupVirtualEnv(path string)`
Creates a virtual environment at the given path by detecting which package manager to use and running the appropriate install command. See package manager detection below.

### `CleanUpVirtualEnv(path string)`
Deletes the `.venv/` directory if it exists at the given path.

### `ExecutePython(path string, scriptName string) ([]byte, error)`
Runs the given Python script using the virtual environment's Python binary. Returns combined stdout+stderr output.

### `GetPythonRunCommand(path string, scriptName string) *exec.Cmd`
Returns an `*exec.Cmd` for running the Python script, allowing the caller to control execution (Start, Wait, pipes, etc.).

## Package Manager Detection & Precedence

`SetupVirtualEnv` checks for indicator files in this order:

| Priority | Tool | Indicator file | Install command |
|----------|------|---------------|-----------------|
| 1 | **uv** | `uv.lock` (+ `pyproject.toml`) | `uv sync` |
| 2 | **Poetry** | `pyproject.toml` with `[tool.poetry]` section | `poetry install` |
| 3 | **pipenv** | `Pipfile` | `pipenv install` |
| 4 | **pip + venv** | `requirements.txt` | `python3 -m venv .venv` + `pip install -r requirements.txt` |

All supported tools install dependencies into `.venv/` in the project directory. `GetPythonRunCommand` / `ExecutePython` use `.venv/bin/python` if `.venv/` exists, otherwise fall back to system `python`.

## Handler Pattern

Every package manager handler follows the same structure:

```go
func xyzProc(path string) {
log.Info("Found '<indicator>'. Creating virtual environment using '<Tool>'.")
cmd := exec.Command("<tool>", "<args>")
cmd.Dir = path
out, err := cmd.CombinedOutput()
if err != nil {
log.Error(fmt.Sprintf("%s", err.Error()))
}
log.Debug(string(out))
}
```

Errors are logged via `log.Error`, not returned. This is the established convention — do not change handlers to return errors.

## Build, Test & Run Commands

```bash
go build ./... # Build
go vet ./... # Vet
go test -v -timeout 120s ./python/ # Run all E2E tests
```

The E2E tests in `python/e2e_test.go` copy each `testdata/<pm>/` fixture into a temp directory, call `SetupVirtualEnv`, run `script.py` via `ExecutePython`, assert an ISO 8601 timestamp is printed, then call `CleanUpVirtualEnv`. Each test skips automatically if the required tool is not on `PATH`.

## Conventions

- Error handling: use `log.Error(fmt.Sprintf(...))` — never return errors from handlers
- New package manager: add `<name>.go` in `python/`, add detection + routing in `pep518.go` or `virtualenv.go`
- Do not modify public function signatures in `virtualenv.go` or `run.go`
- Do not change `ioutil.ReadFile` in `pep518.go` to `os.ReadFile` — leave deprecated API as-is
- Do not fix `fileExists("poetry.toml")` in `poetryProc` — pre-existing CWD-relative behaviour, out of scope

## Commit Rules

- Never add `Co-authored-by` trailers or any co-author lines to commits
67 changes: 0 additions & 67 deletions Pipfile.lock

This file was deleted.

8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
It runs a python script in an isolated environment (virtual environment) via a go wrapper.
It will install dependecies, using one of the following package managers with the following order:

1. Poetry (using `pyproject.toml`)
2. PyFlow (using `pyproject.toml`)
1. uv (using `uv.lock` + `pyproject.toml`)
2. Poetry (using `pyproject.toml`)
3. pipenv (using `Pipfile`)
4. pip & venv (using `requirements.txt`)

Expand All @@ -26,7 +26,7 @@ This will create a vitual enviroment given the existence of proper files (`pypro
python.CleanUpVirtualEnv("/path/to/python/script/")
```

This will delete the `.venv` directory if exists. In case of pyflow, it will also delete the `__pypackages__` directory.
This will delete the `.venv` directory if exists.

#### Run python script inside virtual environment

Expand Down Expand Up @@ -81,8 +81,8 @@ func main() {

### Python package managers

- [uv](https://docs.astral.sh/uv/getting-started/installation/)
- [Poetry](https://python-poetry.org/docs/#installation)
- [PyFlow](https://github.com/David-OConnor/pyflow#installation)
- [pipenv](https://pipenv-fork.readthedocs.io/en/latest/install.html)

### Contribute
Expand Down
36 changes: 0 additions & 36 deletions main.go

This file was deleted.

Loading