Skip to content

Commit ed3fb47

Browse files
committed
Script tp publish to pyPi
1 parent 93c2192 commit ed3fb47

1 file changed

Lines changed: 127 additions & 0 deletions

File tree

publish.sh

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#!/bin/bash
2+
# Publish the `pygad` Python package to PyPI (or TestPyPI).
3+
#
4+
# This is the manual release script. Run it from anywhere, it resolves
5+
# paths relative to its own location and uses whatever Python environment
6+
# is currently active (installing `build` + `twine` into it if they are
7+
# missing).
8+
#
9+
# Pipeline:
10+
# 1. Check build tooling
11+
# 2. Wipe stale dist/ artefacts (confirmation prompt)
12+
# 3. Build sdist + wheel into dist/
13+
# 4. `twine check` the artefacts for README/metadata issues
14+
# 5. Prompt to upload to TestPyPI
15+
# 6. Pause so you can `pip install -i https://test.pypi.org/simple/ pygad`
16+
# in a scratch venv to confirm the release works end-to-end
17+
# 7. Prompt to upload to production PyPI (the irreversible step)
18+
#
19+
# All prompts default to the SAFE answer ('no' for irreversible
20+
# actions) and require a typed 'y' to proceed.
21+
22+
set -euo pipefail
23+
24+
# Colour helpers — silently no-op when stdout isn't a TTY (e.g. piped to
25+
# `tee` or run from a CI runner).
26+
if [ -t 1 ]; then
27+
CYAN='\033[0;36m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
28+
RED='\033[0;31m'; BOLD='\033[1m'; NC='\033[0m'
29+
else
30+
CYAN=''; GREEN=''; YELLOW=''; RED=''; BOLD=''; NC=''
31+
fi
32+
33+
heading() { echo -e "\n${CYAN}${BOLD}== $* ==${NC}"; }
34+
info() { echo -e "${CYAN}$*${NC}"; }
35+
warn() { echo -e "${YELLOW}$*${NC}"; }
36+
error() { echo -e "${RED}$*${NC}" >&2; }
37+
success() { echo -e "${GREEN}$*${NC}"; }
38+
39+
# Default to "no" — caller has to type `y` (case-insensitive) to confirm.
40+
# Used for every destructive / irreversible step.
41+
confirm() {
42+
local prompt="$1"
43+
local answer
44+
read -r -p "$(echo -e "${YELLOW}${prompt} [y/N]:${NC} ")" answer
45+
case "${answer:-}" in
46+
y|Y|yes|YES) return 0 ;;
47+
*) return 1 ;;
48+
esac
49+
}
50+
51+
# Resolve the script's own directory so it works from any cwd.
52+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
53+
54+
# ---- 1. Check build tooling ----
55+
heading "Checking build tooling"
56+
if ! command -v python >/dev/null 2>&1; then
57+
error "No 'python' on PATH. Activate a virtual environment and rerun."
58+
exit 1
59+
fi
60+
info "Active python: $(command -v python)"
61+
info "Active pip: $(command -v pip)"
62+
63+
# Confirm `build` and `twine` are present — install if missing so a
64+
# fresh checkout works without a separate setup step.
65+
if ! python -c "import build" >/dev/null 2>&1 || \
66+
! python -c "import twine" >/dev/null 2>&1; then
67+
warn "Installing missing build tooling (build, twine)..."
68+
pip install --quiet build twine
69+
fi
70+
71+
cd "$SCRIPT_DIR"
72+
73+
# ---- 2. Wipe stale dist/ ----
74+
heading "Cleaning previous artefacts"
75+
if [ -d "dist" ] && [ -n "$(ls -A dist 2>/dev/null)" ]; then
76+
echo "Existing dist/ contents:"
77+
ls -1 dist
78+
if confirm "Delete dist/ before building?"; then
79+
rm -rf dist
80+
success "Removed stale dist/"
81+
else
82+
warn "Keeping existing dist/. Note: twine will refuse to upload duplicates."
83+
fi
84+
else
85+
info "No stale artefacts found."
86+
fi
87+
88+
# ---- 3. Build ----
89+
heading "Building sdist + wheel"
90+
python -m build
91+
ls -1 dist
92+
93+
# ---- 4. twine check ----
94+
heading "Running twine check"
95+
python -m twine check dist/*
96+
97+
# ---- 5. Upload to TestPyPI ----
98+
heading "Upload to TestPyPI"
99+
warn "TestPyPI lives at https://test.pypi.org/ and is the safe place to"
100+
warn "verify the upload before touching production."
101+
if confirm "Upload to TestPyPI now?"; then
102+
python -m twine upload --repository testpypi dist/*
103+
success "Uploaded to TestPyPI."
104+
echo
105+
info "Verify with (in a fresh scratch venv):"
106+
info " pip install --index-url https://test.pypi.org/simple/ \\"
107+
info " --extra-index-url https://pypi.org/simple/ pygad"
108+
echo
109+
read -r -p "Press Enter once the TestPyPI install looks good..."
110+
else
111+
warn "Skipped TestPyPI upload."
112+
fi
113+
114+
# ---- 6. Upload to production PyPI ----
115+
heading "Upload to PRODUCTION PyPI"
116+
warn "This step is IRREVERSIBLE. Once a version is published you cannot"
117+
warn "re-upload the same filename — you'd have to bump the version"
118+
warn "(pyproject.toml, setup.py, and pygad/__init__.py) and rebuild."
119+
warn "Make sure the TestPyPI smoke test passed."
120+
if confirm "Upload to production PyPI now?"; then
121+
python -m twine upload dist/*
122+
success "Uploaded to PyPI: https://pypi.org/project/pygad/"
123+
else
124+
warn "Skipped production upload. Run this script again when ready."
125+
fi
126+
127+
heading "Done"

0 commit comments

Comments
 (0)