Skip to content

Commit 69058ba

Browse files
authored
Update upload_to_pypi.sh
1 parent 3ea882e commit 69058ba

File tree

1 file changed

+222
-34
lines changed

1 file changed

+222
-34
lines changed

src/upload_to_pypi.sh

+222-34
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,280 @@
11
#!/usr/bin/env bash
22

3-
# Exit immediately if a command exits with a non-zero status
4-
set -e
3+
# Script Name: release_package.sh
4+
# Description: Automates the process of building, checking, and releasing a Python package to PyPI or TestPyPI.
5+
#
6+
# Usage: ./release_package.sh [options]
7+
#
8+
# Options:
9+
# -h, --help Show this help message and exit.
10+
# -t, --test Upload the package to TestPyPI.
11+
# -p, --production Upload the package to PyPI.
12+
# -s, --skip-tests Skip running tests before building.
13+
# -v, --version VERSION Specify the package version to release.
14+
# -n, --name NAME Specify the package name.
15+
# -c, --config FILE Specify a configuration file with default settings.
16+
# -e, --env VENV_PATH Specify a virtual environment to use.
17+
# -i, --interpreter PATH Specify the Python interpreter to use.
18+
# --dry-run Perform a dry run without uploading.
19+
#
20+
# Examples:
21+
# ./release_package.sh --test
22+
# ./release_package.sh --production --version 1.0.0 --name my-package
23+
# ./release_package.sh --config release.conf --production
24+
25+
set -euo pipefail
526

627
# Color codes for output
728
GREEN='\033[0;32m'
29+
RED='\033[0;31m'
830
NC='\033[0m' # No Color
931

10-
# Function to ensure a command exists, if not, install it
11-
function ensure_installed() {
12-
if ! command -v "$1" &> /dev/null; then
13-
echo -e "${GREEN}$1 not found. Installing...${NC}"
14-
pip install "$1"
32+
# Default values
33+
UPLOAD_TO_TEST=false
34+
UPLOAD_TO_PROD=false
35+
SKIP_TESTS=false
36+
PACKAGE_VERSION=""
37+
PACKAGE_NAME=""
38+
CONFIG_FILE=""
39+
VENV_PATH=""
40+
PYTHON_INTERPRETER="python"
41+
DRY_RUN=false
42+
43+
# Function to display help message
44+
function show_help() {
45+
grep '^#' "$0" | cut -c 4-
46+
exit 0
47+
}
48+
49+
# Function to ensure a Python package is installed
50+
function ensure_python_package() {
51+
local package="$1"
52+
if ! "$PYTHON_INTERPRETER" -c "import $package" &> /dev/null; then
53+
echo -e "${GREEN}Installing Python package '$package'...${NC}"
54+
"$PYTHON_INTERPRETER" -m pip install --upgrade "$package"
55+
fi
56+
}
57+
58+
# Function to run tests
59+
function run_tests() {
60+
if [ -f "setup.py" ]; then
61+
echo -e "${GREEN}Running tests...${NC}"
62+
"$PYTHON_INTERPRETER" setup.py test
63+
elif [ -f "pytest.ini" ] || [ -d "tests" ]; then
64+
echo -e "${GREEN}Running pytest...${NC}"
65+
ensure_python_package pytest
66+
"$PYTHON_INTERPRETER" -m pytest
67+
else
68+
echo -e "${RED}No tests found.${NC}"
1569
fi
1670
}
1771

1872
# Function to build the package
1973
function build_package() {
2074
echo -e "${GREEN}Building distribution packages...${NC}"
21-
ensure_installed build
22-
python -m build
75+
ensure_python_package build
76+
rm -rf dist/ build/
77+
"$PYTHON_INTERPRETER" -m build
78+
}
79+
80+
# Function to check the built package
81+
function check_package() {
82+
echo -e "${GREEN}Checking distribution packages...${NC}"
83+
ensure_python_package twine
84+
"$PYTHON_INTERPRETER" -m twine check dist/*
2385
}
2486

2587
# Function to upload to TestPyPI
2688
function upload_testpypi() {
2789
echo -e "${GREEN}Uploading to TestPyPI...${NC}"
28-
twine upload --repository testpypi dist/*
29-
echo -e "${GREEN}Package uploaded to TestPyPI.${NC}"
30-
echo -e "${GREEN}You can install it using:${NC}"
31-
echo "pip install --index-url https://test.pypi.org/simple/ --no-deps your-package-name"
90+
if [ "$DRY_RUN" = true ]; then
91+
echo -e "${GREEN}[Dry Run] Skipping upload to TestPyPI.${NC}"
92+
else
93+
"$PYTHON_INTERPRETER" -m twine upload --repository testpypi dist/*
94+
echo -e "${GREEN}Package uploaded to TestPyPI.${NC}"
95+
if [ -n "$PACKAGE_NAME" ]; then
96+
echo -e "${GREEN}You can install it using:${NC}"
97+
echo "pip install --index-url https://test.pypi.org/simple/ --no-deps $PACKAGE_NAME"
98+
fi
99+
fi
32100
}
33101

34102
# Function to upload to PyPI
35103
function upload_pypi() {
36104
echo -e "${GREEN}Uploading to PyPI...${NC}"
37-
twine upload dist/*
38-
echo -e "${GREEN}Package uploaded to PyPI.${NC}"
105+
if [ "$DRY_RUN" = true ]; then
106+
echo -e "${GREEN}[Dry Run] Skipping upload to PyPI.${NC}"
107+
else
108+
"$PYTHON_INTERPRETER" -m twine upload dist/*
109+
echo -e "${GREEN}Package uploaded to PyPI.${NC}"
110+
fi
39111
}
40112

41113
# Function to check for uncommitted changes
42114
function check_git_status() {
43-
if [ -n "$(git status --porcelain)" ]; then
44-
echo -e "${GREEN}Uncommitted changes detected. Please commit or stash them before releasing.${NC}"
45-
exit 1
115+
if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
116+
if [ -n "$(git status --porcelain)" ]; then
117+
echo -e "${RED}Uncommitted changes detected. Please commit or stash them before releasing.${NC}"
118+
exit 1
119+
fi
120+
else
121+
echo -e "${RED}Not a git repository. Skipping git status check.${NC}"
46122
fi
47123
}
48124

49125
# Function to confirm the package version
50126
function confirm_version() {
51-
PACKAGE_VERSION=$(python setup.py --version)
52-
echo -e "${GREEN}Current package version is: $PACKAGE_VERSION${NC}"
127+
local version
128+
if [ -n "$PACKAGE_VERSION" ]; then
129+
version="$PACKAGE_VERSION"
130+
else
131+
version=$("$PYTHON_INTERPRETER" setup.py --version 2>/dev/null || echo "Unknown")
132+
fi
133+
echo -e "${GREEN}Current package version is: $version${NC}"
53134
read -p "Is this the correct version to upload? (y/n): " confirm_version
54135
if [ "$confirm_version" != "y" ]; then
55-
echo -e "${GREEN}Please update your package version before proceeding.${NC}"
136+
echo -e "${RED}Please update your package version before proceeding.${NC}"
137+
exit 1
138+
fi
139+
}
140+
141+
# Function to parse the configuration file
142+
function parse_config() {
143+
if [ -f "$CONFIG_FILE" ]; then
144+
echo -e "${GREEN}Loading configuration from $CONFIG_FILE...${NC}"
145+
while IFS='=' read -r key value; do
146+
case "$key" in
147+
upload_to_test) UPLOAD_TO_TEST="$value" ;;
148+
upload_to_prod) UPLOAD_TO_PROD="$value" ;;
149+
skip_tests) SKIP_TESTS="$value" ;;
150+
package_version) PACKAGE_VERSION="$value" ;;
151+
package_name) PACKAGE_NAME="$value" ;;
152+
python_interpreter) PYTHON_INTERPRETER="$value" ;;
153+
venv_path) VENV_PATH="$value" ;;
154+
dry_run) DRY_RUN="$value" ;;
155+
esac
156+
done < "$CONFIG_FILE"
157+
else
158+
echo -e "${RED}Configuration file $CONFIG_FILE not found.${NC}"
56159
exit 1
57160
fi
58161
}
59162

163+
# Parse command-line arguments
164+
function parse_args() {
165+
while [[ $# -gt 0 ]]; do
166+
case $1 in
167+
-h|--help)
168+
show_help
169+
;;
170+
-t|--test)
171+
UPLOAD_TO_TEST=true
172+
shift
173+
;;
174+
-p|--production)
175+
UPLOAD_TO_PROD=true
176+
shift
177+
;;
178+
-s|--skip-tests)
179+
SKIP_TESTS=true
180+
shift
181+
;;
182+
-v|--version)
183+
PACKAGE_VERSION="$2"
184+
shift 2
185+
;;
186+
-n|--name)
187+
PACKAGE_NAME="$2"
188+
shift 2
189+
;;
190+
-c|--config)
191+
CONFIG_FILE="$2"
192+
shift 2
193+
;;
194+
-e|--env)
195+
VENV_PATH="$2"
196+
shift 2
197+
;;
198+
-i|--interpreter)
199+
PYTHON_INTERPRETER="$2"
200+
shift 2
201+
;;
202+
--dry-run)
203+
DRY_RUN=true
204+
shift
205+
;;
206+
*)
207+
echo -e "${RED}Unknown option: $1${NC}"
208+
show_help
209+
;;
210+
esac
211+
done
212+
}
213+
214+
# Function to activate virtual environment if specified
215+
function activate_virtualenv() {
216+
if [ -n "$VENV_PATH" ]; then
217+
if [ -d "$VENV_PATH" ]; then
218+
source "$VENV_PATH/bin/activate"
219+
PYTHON_INTERPRETER="$VENV_PATH/bin/python"
220+
echo -e "${GREEN}Using virtual environment at $VENV_PATH${NC}"
221+
else
222+
echo -e "${RED}Virtual environment at $VENV_PATH not found.${NC}"
223+
exit 1
224+
fi
225+
fi
226+
}
227+
60228
# Main script execution
61229
function main() {
230+
parse_args "$@"
231+
232+
# Parse configuration file if specified
233+
if [ -n "$CONFIG_FILE" ]; then
234+
parse_config
235+
fi
236+
237+
# Activate virtual environment if specified
238+
activate_virtualenv
239+
62240
# Check for uncommitted changes
63241
check_git_status
64242

65243
# Ensure required tools are installed
66-
ensure_installed twine
244+
ensure_python_package pip
245+
ensure_python_package setuptools
246+
ensure_python_package wheel
247+
ensure_python_package twine
248+
249+
# Run tests unless skipped
250+
if [ "$SKIP_TESTS" = false ]; then
251+
run_tests
252+
fi
67253

68254
# Build the package
69255
build_package
70256

71-
# Optionally upload to TestPyPI
72-
read -p "Do you want to upload to TestPyPI first? (y/n): " upload_test
73-
if [ "$upload_test" = "y" ]; then
74-
upload_testpypi
75-
fi
257+
# Check the built package
258+
check_package
76259

77-
# Confirm version before uploading to PyPI
260+
# Confirm version before uploading
78261
confirm_version
79262

80-
# Upload to PyPI
81-
read -p "Ready to upload to PyPI. Proceed? (y/n): " upload_pypi
82-
if [ "$upload_pypi" = "y" ]; then
263+
# Upload to TestPyPI or PyPI
264+
if [ "$UPLOAD_TO_TEST" = true ]; then
265+
upload_testpypi
266+
fi
267+
268+
if [ "$UPLOAD_TO_PROD" = true ]; then
83269
upload_pypi
84-
else
85-
echo -e "${GREEN}Upload to PyPI aborted.${NC}"
270+
fi
271+
272+
if [ "$UPLOAD_TO_TEST" = false ] && [ "$UPLOAD_TO_PROD" = false ]; then
273+
echo -e "${GREEN}Build completed. Packages are available in the 'dist/' directory.${NC}"
86274
fi
87275

88276
echo -e "${GREEN}Script completed.${NC}"
89277
}
90278

91279
# Run the main function
92-
main
280+
main "$@"

0 commit comments

Comments
 (0)