Skip to content
Merged

Sync #357

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
6 changes: 5 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,13 @@
python3 -m build

# Install the newly built .whl file to verify the package is installable.
# Install with the 'visualize' extra so matplotlib is available for the
# plotting tests (tests/test_visualize.py). This also validates that the
# optional 'visualize' extra resolves correctly.
- name: Install PyGAD from Wheel
run: |
pip install dist/*.whl
WHEEL=$(ls dist/*.whl)
pip install "${WHEEL}[visualize]"

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 0: pipCommand not pinned by hash
Click Remediation section below to solve this issue

- name: Install PyTest
run: pip install pytest
Expand Down
68 changes: 68 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
name: release

Check failure

Code scanning / Scorecard

Token-Permissions High

score is 0: no topLevel permission defined
Remediation tip: Visit https://app.stepsecurity.io/secureworkflow.
Tick the 'Restrict permissions for GITHUB_TOKEN'
Untick other options
NOTE: If you want to resolve multiple issues at once, you can visit https://app.stepsecurity.io/securerepo instead.
Click Remediation section below for further remediation help

# On a version tag this builds the package once, publishes it to PyPI via
# trusted publishing (no API token stored in the repo), and attaches the built
# wheel and sdist to a GitHub Release for the tag. The PyPI project must list
# this repo and workflow as a trusted publisher first.

on:
push:
# PyGAD tags releases as the bare version number, e.g. 3.6.0 (no "v").
tags:
- '[0-9]+.[0-9]+.[0-9]+'

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 0: GitHub-owned GitHubAction not pinned by hash
Remediation tip: update your workflow using https://app.stepsecurity.io
Click Remediation section below for further remediation help
- uses: actions/setup-python@v5

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 0: GitHub-owned GitHubAction not pinned by hash
Remediation tip: update your workflow using https://app.stepsecurity.io
Click Remediation section below for further remediation help
with:
python-version: "3.12"
- name: Build distributions
run: |
pip install build

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 0: pipCommand not pinned by hash
Click Remediation section below to solve this issue
python -m build
- name: Check distributions
run: |
pip install twine

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 0: pipCommand not pinned by hash
Click Remediation section below to solve this issue
python -m twine check dist/*
- uses: actions/upload-artifact@v4

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 0: GitHub-owned GitHubAction not pinned by hash
Remediation tip: update your workflow using https://app.stepsecurity.io
Click Remediation section below for further remediation help
with:
name: dist
path: dist/

publish:
needs: build
runs-on: ubuntu-latest
environment: pypi
permissions:
id-token: write
steps:
- uses: actions/download-artifact@v4

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 0: GitHub-owned GitHubAction not pinned by hash
Remediation tip: update your workflow using https://app.stepsecurity.io
Click Remediation section below for further remediation help
with:
name: dist
path: dist/
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 0: third-party GitHubAction not pinned by hash
Remediation tip: update your workflow using https://app.stepsecurity.io
Click Remediation section below for further remediation help

github-release:
needs: build
runs-on: ubuntu-latest
permissions:
contents: write

Check failure

Code scanning / Scorecard

Token-Permissions High

score is 0: jobLevel 'contents' permission set to 'write'
Remediation tip: Verify which permissions are needed and consider whether you can reduce them.
Click Remediation section below for further remediation help
steps:
- uses: actions/download-artifact@v4

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 0: GitHub-owned GitHubAction not pinned by hash
Remediation tip: update your workflow using https://app.stepsecurity.io
Click Remediation section below for further remediation help
with:
name: dist
path: dist/
- name: Attach the built files to the GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
gh release create "$GITHUB_REF_NAME" dist/* \
--repo "$GITHUB_REPOSITORY" \
--title "$GITHUB_REF_NAME" \
--generate-notes \
|| gh release upload "$GITHUB_REF_NAME" dist/* \
--repo "$GITHUB_REPOSITORY" --clobber
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ Install PyGAD with the following command:
pip install pygad
```

PyGAD's core install is intentionally lightweight (only `numpy` and `cloudpickle`). Some features need extra libraries, which are available as optional extras:

```
# Plotting features (e.g. plot_fitness(), plot_genes()) need matplotlib:
pip install pygad[visualize]

# Training Keras/PyTorch models (pygad.kerasga, pygad.torchga):
pip install pygad[deep_learning]
```

To get started with PyGAD, read the documentation at [Read the Docs](https://pygad.readthedocs.io).

# PyGAD Source Code
Expand Down
51 changes: 51 additions & 0 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Releasing

Releases are automated. Pushing a version tag builds the package, publishes it to
PyPI, and attaches the built files to a GitHub Release. Nothing is uploaded by
hand.

## Steps

1. Bump the version in `pygad/_version.py`. This is the only place the version
lives.
2. Update the release notes in the docs if you keep them there.
3. Commit and push:
```bash
git add pygad/_version.py
git commit -m "Release 3.6.1"
git push
```
4. Wait for the test workflow (`main.yml`) to pass on that commit.
5. Tag the release and push the tag:
```bash
git tag 3.6.1
git push origin 3.6.1
```

The `release` workflow does the rest: it builds the wheel and sdist, publishes
them to PyPI, and creates a GitHub Release with both files attached. Follow it
with `gh run watch` or the Actions tab.

## Rules

- The tag must match `pygad/_version.py` and is the bare version number with no
`v` prefix, for example `3.6.1`. The tag is what triggers the release.
- Every release needs a new version number. PyPI does not allow re-uploading or
overwriting a version that already exists.
- Do not run `twine upload` or upload files to the GitHub Release by hand. The
tag does both for you.

## Manual fallback

`publish.sh` can build and upload to PyPI from your machine if you ever need it.

## One-time setup (maintainers)

Done once per project. No API token is involved, because PyPI trusted publishing
is tokenless.

- On the PyPI `pygad` project, open Settings, then Publishing, and add a GitHub
publisher: owner `ahmedfgad`, repository `GeneticAlgorithmPython`, workflow
`release.yml`, environment `pypi`.
- In the GitHub repo, open Settings, then Environments, and create an environment
named `pypi`.
127 changes: 127 additions & 0 deletions publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/bin/bash
# Publish the `pygad` Python package to PyPI (or TestPyPI).
#
# This is the manual release script. Run it from anywhere, it resolves
# paths relative to its own location and uses whatever Python environment
# is currently active (installing `build` + `twine` into it if they are
# missing).
#
# Pipeline:
# 1. Check build tooling
# 2. Wipe stale dist/ artefacts (confirmation prompt)
# 3. Build sdist + wheel into dist/
# 4. `twine check` the artefacts for README/metadata issues
# 5. Prompt to upload to TestPyPI
# 6. Pause so you can `pip install -i https://test.pypi.org/simple/ pygad`
# in a scratch venv to confirm the release works end-to-end
# 7. Prompt to upload to production PyPI (the irreversible step)
#
# All prompts default to the SAFE answer ('no' for irreversible
# actions) and require a typed 'y' to proceed.

set -euo pipefail

# Colour helpers — silently no-op when stdout isn't a TTY (e.g. piped to
# `tee` or run from a CI runner).
if [ -t 1 ]; then
CYAN='\033[0;36m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
RED='\033[0;31m'; BOLD='\033[1m'; NC='\033[0m'
else
CYAN=''; GREEN=''; YELLOW=''; RED=''; BOLD=''; NC=''
fi

heading() { echo -e "\n${CYAN}${BOLD}== $* ==${NC}"; }
info() { echo -e "${CYAN}$*${NC}"; }
warn() { echo -e "${YELLOW}$*${NC}"; }
error() { echo -e "${RED}$*${NC}" >&2; }
success() { echo -e "${GREEN}$*${NC}"; }

# Default to "no" — caller has to type `y` (case-insensitive) to confirm.
# Used for every destructive / irreversible step.
confirm() {
local prompt="$1"
local answer
read -r -p "$(echo -e "${YELLOW}${prompt} [y/N]:${NC} ")" answer
case "${answer:-}" in
y|Y|yes|YES) return 0 ;;
*) return 1 ;;
esac
}

# Resolve the script's own directory so it works from any cwd.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# ---- 1. Check build tooling ----
heading "Checking build tooling"
if ! command -v python >/dev/null 2>&1; then
error "No 'python' on PATH. Activate a virtual environment and rerun."
exit 1
fi
info "Active python: $(command -v python)"
info "Active pip: $(command -v pip)"

# Confirm `build` and `twine` are present — install if missing so a
# fresh checkout works without a separate setup step.
if ! python -c "import build" >/dev/null 2>&1 || \
! python -c "import twine" >/dev/null 2>&1; then
warn "Installing missing build tooling (build, twine)..."
pip install --quiet build twine

Check warning

Code scanning / Scorecard

Pinned-Dependencies Medium

score is 0: pipCommand not pinned by hash
Click Remediation section below to solve this issue
fi

cd "$SCRIPT_DIR"

# ---- 2. Wipe stale dist/ ----
heading "Cleaning previous artefacts"
if [ -d "dist" ] && [ -n "$(ls -A dist 2>/dev/null)" ]; then
echo "Existing dist/ contents:"
ls -1 dist
if confirm "Delete dist/ before building?"; then
rm -rf dist
success "Removed stale dist/"
else
warn "Keeping existing dist/. Note: twine will refuse to upload duplicates."
fi
else
info "No stale artefacts found."
fi

# ---- 3. Build ----
heading "Building sdist + wheel"
python -m build
ls -1 dist

# ---- 4. twine check ----
heading "Running twine check"
python -m twine check dist/*

# ---- 5. Upload to TestPyPI ----
heading "Upload to TestPyPI"
warn "TestPyPI lives at https://test.pypi.org/ and is the safe place to"
warn "verify the upload before touching production."
if confirm "Upload to TestPyPI now?"; then
python -m twine upload --repository testpypi dist/*
success "Uploaded to TestPyPI."
echo
info "Verify with (in a fresh scratch venv):"
info " pip install --index-url https://test.pypi.org/simple/ \\"
info " --extra-index-url https://pypi.org/simple/ pygad"
echo
read -r -p "Press Enter once the TestPyPI install looks good..."
else
warn "Skipped TestPyPI upload."
fi

# ---- 6. Upload to production PyPI ----
heading "Upload to PRODUCTION PyPI"
warn "This step is IRREVERSIBLE. Once a version is published you cannot"
warn "re-upload the same filename — you'd have to bump the version"
warn "(pyproject.toml, setup.py, and pygad/__init__.py) and rebuild."
warn "Make sure the TestPyPI smoke test passed."
if confirm "Upload to production PyPI now?"; then
python -m twine upload dist/*
success "Uploaded to PyPI: https://pypi.org/project/pygad/"
else
warn "Skipped production upload. Run this script again when ready."
fi

heading "Done"
2 changes: 1 addition & 1 deletion pygad/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
from .pygad import * # Relative import.

__version__ = "3.6.0"
from ._version import __version__
1 change: 1 addition & 0 deletions pygad/_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "3.6.0"
9 changes: 6 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "pygad"
version = "3.6.0"
dynamic = ["version"]
description = "PyGAD: A Python Library for Building the Genetic Algorithm and Training Machine Learning Algoithms (Keras & PyTorch)."
readme = {file = "README.md", content-type = "text/markdown"}
requires-python = ">=3"
Expand Down Expand Up @@ -42,7 +42,6 @@ classifiers = [
keywords = ["genetic algorithm", "GA", "optimization", "evolutionary algorithm", "natural evolution", "pygad", "machine learning", "deep learning", "neural networks", "tensorflow", "keras", "pytorch"]
dependencies = [
"numpy",
"matplotlib",
"cloudpickle",
]

Expand All @@ -58,7 +57,11 @@ dependencies = [

[project.optional-dependencies]
deep_learning = ["keras", "tensorflow", "torch"]
visualize = ["matplotlib"]

# PyTest Configuration. Later, PyTest will support the [tool.pytest] table.
[tool.pytest.ini_options]
testpaths = ["tests"]
testpaths = ["tests"]

[tool.setuptools.dynamic]
version = { attr = "pygad._version.__version__" }
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
numpy
matplotlib
cloudpickle
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

setuptools.setup(
name="pygad",
version="3.6.0",
author="Ahmed Fawzy Gad",
install_requires=["numpy", "matplotlib", "cloudpickle",],
install_requires=["numpy", "cloudpickle",],
extras_require={
"deep_learning": ["keras", "tensorflow", "torch"],
"visualize": ["matplotlib"],
},
author_email="ahmed.f.gad@gmail.com",

Expand Down
Loading