Last active
April 22, 2025 02:09
-
-
Save dotysan/44589bcf6064d92baf6f4372c1907759 to your computer and use it in GitHub Desktop.
Bootstrap a Python project from scratch.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env bash | |
# | |
# Bootstrap a Python project from scratch. | |
# | |
# optional | |
# DEBUG=yep | |
# VERBOSE=--verbose | |
# mandatory | |
PYVER=3.13.3t | |
main() { | |
chk_deps | |
get_uv | |
uv_init | |
ruff_init | |
# git status | |
} | |
#======================================================================= | |
chk_deps() { | |
chkadep curl | |
chkadep jq | |
chkadep uname | |
} | |
get_uv() { | |
# Most modern linux distros will add ~/.local/bin to the $PATH on | |
# next login--as soon as it exists. But if we just created it, | |
# inject the $PATH now, so we don't need to log out & back in! | |
if [[ -d "$HOME/.local/bin" ]] && ! grep -qE "(^|:)$HOME/.local/bin:" <<<"$PATH" | |
then export PATH="$HOME/.local/bin:$PATH" | |
fi | |
if ! chkadep uv uvx | |
then | |
get_github_latest astral-sh uv | |
create_uv_receipt | |
# we just installed it | |
if ! grep -qE "(^|:)$HOME/.local/bin:" <<<"$PATH" | |
then export PATH="$HOME/.local/bin:$PATH" | |
fi | |
fi | |
} | |
uv_init() { | |
local cmd="uv ${VERBOSE:-} init --python=$PYVER --lib --author-from=auto --build-backend=flit" | |
$cmd | |
if [[ ! -s README.md ]] | |
then echo "# ${PWD##*/}" >>README.md | |
fi | |
git add .python-version README.md pyproject.toml src | |
git add .gitignore ||: # won't exist if its already in parent dir | |
git commit -m "$cmd" | |
local pyver_re="${PYVER//./\\.}" | |
if ! grep -q "^$pyver_re\$" .python-version | |
then | |
echo "$PYVER" >.python-version | |
git add .python-version | |
git commit -m 'workaround uv bug https://github.com/astral-sh/uv/issues/12855' | |
fi | |
} | |
ruff_init() { | |
# this also builds the .venv/ | |
cmd="uv ${VERBOSE:-} add --dev ruff tomlkit" | |
$cmd | |
git add pyproject.toml uv.lock | |
git commit -m "$cmd" | |
uv ${VERBOSE:-} run python - <<-EOF | |
from tomlkit import parse, dumps, table, nl, comment | |
with open('pyproject.toml', encoding='utf-8') as f: | |
doc = parse(f.read()) | |
ruff = table() | |
ruff.add(nl()) | |
ruff.add(comment('GitHub standard')) | |
ruff.add('line-length', 127) | |
fmt = table() | |
fmt.add(nl()) | |
fmt.add(comment('prefer single over double quotes')) | |
fmt.add('quote-style', 'single') | |
ruff['format'] = fmt | |
doc.add(nl()) | |
doc.add(nl()) | |
doc.setdefault('tool', {})['ruff'] = ruff | |
with open('pyproject.toml', mode='w', encoding='utf-8') as f: | |
f.write(dumps(doc)) | |
EOF | |
git add pyproject.toml | |
git commit -m 'set ruff stuff' | |
#uv ${VERBOSE:-} run ruff check | |
} | |
#----------------------------------------------------------------------- | |
chkadep() { | |
if ! hash "$@" | |
then | |
echo "ERROR: Must install $@ to use this script." | |
return 1 | |
fi >&2 | |
} | |
get_github_latest() { | |
local owner="$1" | |
local repo="$2" | |
local os=$(uname --operating-system) | |
if ! [[ ${os,,} =~ linux ]] | |
then | |
echo "ERROR: OS:$os This script only understands linux." | |
return 1 | |
fi >&2 | |
local latest_url="https://api.github.com/repos/$owner/$repo/releases/latest" | |
VERS=$(curl --silent "$latest_url" |jq --raw-output .name) | |
local machine=$(uname --machine) | |
local asset="https://github.com/$owner/$repo/releases/download/$VERS/$repo-$machine-unknown-linux-gnu.tar.gz" | |
mkdir --parents "$HOME/.local/bin" | |
curl --silent --location "$asset" |\ | |
tar --extract --gzip --directory="$HOME/.local/bin" --strip-components=1 | |
} | |
create_uv_receipt() { | |
mkdir --parents "$HOME/.config/uv" | |
cat >"$HOME/.config/uv/uv-receipt.json" <<-EOF | |
{ | |
"binaries": ["uv", "uvx"], | |
"install_layout": "flat", | |
"install_prefix": "$HOME/.local/bin", | |
"modify_path": false, | |
"provider": { | |
"source": "dotysan", | |
"version": "0.1.0" | |
}, | |
"source": { | |
"app_name": "uv", | |
"name": "uv", | |
"owner": "astral-sh", | |
"release_type": "github" | |
}, | |
"version": "$VERS" | |
} | |
EOF | |
} | |
#======================================================================= | |
if [ "${BASH_VERSINFO[0]}" != "5" ] | |
then | |
echo 'This script only tested with Bourne-Again Shell v5.' | |
exit 1 | |
fi >&2 | |
# poor man's __main__ | |
return 2>/dev/null ||: | |
set -o errexit | |
set -o nounset | |
set -o pipefail | |
if [[ "${DEBUG:-}" ]] | |
then | |
PS4func() { | |
local lineno="$1" | |
local i f='' | |
local c="\033[0;36m" y="\033[0;33m" n="\033[0m" | |
local d=$((${#FUNCNAME[@]}-2)) | |
if [[ $lineno == 1 ]] | |
then lineno=0 | |
fi | |
for ((i=d; i>0; i--)) | |
do printf -v f "%s%s()" "$f" "${FUNCNAME[i]}" | |
done | |
printf "$y%s:%04d$c%s$n " "${BASH_SOURCE[1]##*/}" "$lineno" "$f" | |
} | |
PS4='\r$(PS4func $LINENO)' | |
set -o xtrace | |
fi | |
main | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment