Last active
June 3, 2022 02:54
-
-
Save snejus/ec6edb43fc8bd7874313559d9a159d7e to your computer and use it in GitHub Desktop.
Find and print the changelog of a python package
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 | |
# Try finding the changelog of a Python package and render it with glow (markdown) or | |
# pygments (md and rst), if they are installed. | |
# | |
# Tested with bash and zsh. It isn't POSIX-compatible however. | |
# | |
# ### clone the gist | |
# $ chmod a+x changelog | |
# $ ln -is `realpath changelog` ~/.local/bin/changelog | |
# | |
# USAGE: | |
# | |
# $ changelog <package-name> [-p|--pager] [-P|--plain] [-s|--silent] [-h|--help] | |
# | |
# (or just source the functions to your environment) | |
# | |
# It depends on libraries providing links to their *github* repos in the 'Home-page' | |
# field on PyPI. For those which don't, there's a CHANGELOGS variable that allows to | |
# hard-code the mappings. | |
# | |
# It tries using Dephell to find the package details (since it doesn't need the package | |
# installed in order to `dephell show <pkg>`, unlike pip). If Dephell isn't available, it | |
# uses pip. | |
# | |
# If the changelog isn't found in the repo, it prints out the README, given it exists. | |
# | |
# If you want to access private repos and increase the Github API's rate limits, | |
# | |
# $ export GITHUB_TOKEN=<your github token> | |
# | |
declare -A CHANGELOGS | |
CHANGELOGS[attrs]="python-attrs/attrs/master/CHANGELOG.rst" | |
CHANGELOGS[isort]="PyCQA/isort/develop/CHANGELOG.md" | |
CHANGELOGS[pip]="pypa/pip/master/NEWS.rst" | |
CHANGELOGS[poetry]="python-poetry/poetry/master/CHANGELOG.md" | |
CHANGELOGS[psycopg2]="psycopg/psycopg2/master/NEWS" | |
CHANGELOGS[psycopg2-binary]="psycopg/psycopg2/master/NEWS" | |
good() { | |
printf "\e[1;3;32m$1\e[0m" | |
} | |
okay() { | |
printf "\e[1;33m$1\e[0m" | |
} | |
oh() { | |
printf "\e[1;31m$1\e[0m" | |
} | |
happy() { | |
printf "$(good "$1")" | |
[[ -n $2 ]] && printf " $2\n" || printf "\n" | |
} | |
hmmm() { | |
printf >&2 "$(okay WARNING:) $1\n" | |
} | |
uhoh() { | |
printf >&2 "$(oh ERROR:) $1\n" | |
} | |
parse_homepage() { | |
sed -n 's/.*\"\?Home-\?page\"\?: //pI' | tr -d '",' | |
} | |
get_homepage() { | |
pkg=$1 | |
if which dephell &> /dev/null; then | |
details=$(dephell package show "$pkg" 2> /dev/null) | |
[[ -n $details ]] && echo "$details" | parse_homepage && return | |
hmmm "Dephell didn't find the package: Using pip" | |
else | |
hmmm "Dephell executable not found: Using pip" | |
fi | |
details=$(pip show "$pkg" 2> /dev/null) | |
[[ -n $details ]] && echo "$details" | parse_homepage | |
} | |
get() { | |
curl -s -u "username:$GITHUB_TOKEN" "$(echo "$1" | tr -d ' "')" | |
} | |
show() { | |
# Markdown: glow -> pygments -> plain | |
# Restructured text: pygments -> plain | |
url="$1" | |
plain=$2 | |
[[ $plain -eq 1 ]] && get "$url" && return | |
format="rst" | |
echo "$url" | grep -vq ".rst" && format="md" | |
if [[ $format == "md" ]] && which glow &> /dev/null; then | |
get "$url" | glow -s dark - && return 0 | |
fi | |
if which pygmentize &> /dev/null; then | |
get "$url" | pygmentize -l "$format" -O style=monokai | |
else | |
get "$url" | |
fi | |
} | |
render() { | |
url=$1 | |
pager=$2 | |
plain=$3 | |
if [[ $pager -eq 0 ]]; then | |
show "$url" $plain 1>&2 | |
else | |
printf "$(show "$url" $plain)" | less 1>&2 | |
fi | |
} | |
changelog() { | |
pkg=$1 | |
pager=$2 | |
plain=$3 | |
printf "Looking for $(good "$pkg") changelog\n" | |
[[ -z $GITHUB_TOKEN ]] && hmmm "GITHUB_TOKEN is not found in the environment" | |
if [ ${CHANGELOGS[$pkg]} ]; then | |
happy "Found it in the pre-defined changelogs list" | |
render "https://raw.githubusercontent.com/${CHANGELOGS[$pkg]}" $pager $plain | |
exit 0 | |
fi | |
pat_projectname='s/^.*hub\.com\/\(.*\/\?\$\?\)/\1/pI' | |
pat_changelogurl='s/download_url.*\(https.*change.*\),/\1/pI' | |
pat_downloadurl='s/download_url.*\(https.*\),/\1/pI' | |
homepage="$(get_homepage $pkg)" | |
[[ -z $homepage ]] && uhoh "Pip failed to find the package" && exit 1 | |
happy "Home-page:" "$homepage" | |
projectname=$(echo "$homepage" | sed -n "$pat_projectname") | |
[[ -z $projectname ]] && \ | |
uhoh "Could not find $pkg github repository. Check out its home page instead: $homepage" && \ | |
exit 1 | |
happy "Github project:" "$projectname" | |
changelogurl=$(get "https://api.github.com/repos/$projectname/contents" | sed -n "$pat_changelogurl") | |
if [[ -n $changelogurl ]]; then | |
happy "Found the changelog" | |
url="$changelogurl" | |
else | |
hmmm "Failed to find the changelog in the repository root. Looking for a readme instead." | |
readmeurl=$(get "https://api.github.com/repos/$projectname/readme" | sed -n "$pat_downloadurl") | |
[[ -z $readmeurl ]] && uhoh "❓ $pkg doesn't have a readme❓" && exit 1 | |
happy 'Found the readme' | |
url="$readmeurl" | |
fi | |
render "$url" $pager $plain | |
exit 0 | |
} | |
chill() { | |
printf "\e[1;34m$1\e[0m" | |
} | |
comment() { | |
printf "\e[1;3;90m$1\e[0m" | |
} | |
show_help() { | |
cat <<-EOF | |
» $(chill "changelog") $(okay "[options]") $(good "package") $(okay "[options]") | |
┌ $(okay "-p, --pager") $(comment "# page the output") | |
├ $(okay "-P, --plain") $(comment "# do not apply post-processing") | |
├ $(okay "-s, --silent") $(comment "# only print errors and the output") | |
└ $(okay "-h, --help") $(comment "# display help") | |
EOF | |
exit 0 | |
} | |
unhappy_input() { | |
uhoh "$1\n" | |
chill " changelog" | |
oh " package\n\n" | |
} | |
if test $# -eq 0; then | |
show_help | |
else | |
pager=0 | |
plain=0 | |
silent=0 | |
pkg="" | |
while test $# -ne 0; do | |
case $1 in | |
-p|--pager) pager=1 ;; | |
-P|--plain) plain=1 ;; | |
-s|--silent) silent=1 ;; | |
-h|--help) show_help; exit ;; | |
*) | |
if [[ -z $pkg ]]; then | |
pkg="$1" | |
else | |
unhappy_input "Please provide a single package name" | |
exit 1 | |
fi; ;; | |
esac | |
shift | |
done | |
[[ -z $pkg ]] && unhappy_input "Please provide a package name" && exit 1 | |
if [[ $silent -eq 1 ]]; then | |
changelog $pkg $pager $plain 1> /dev/null | |
else | |
changelog $pkg $pager $plain | |
fi | |
fi | |
# vim:ft=bash |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment