Skip to content

Instantly share code, notes, and snippets.

@dotysan
Last active April 22, 2025 02:09
Show Gist options
  • Save dotysan/44589bcf6064d92baf6f4372c1907759 to your computer and use it in GitHub Desktop.
Save dotysan/44589bcf6064d92baf6f4372c1907759 to your computer and use it in GitHub Desktop.
Bootstrap a Python project from scratch.
#! /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