Last active
May 20, 2025 11:20
-
-
Save kopach/53168f97cdf8fa91c0998d44f06d15af to your computer and use it in GitHub Desktop.
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
#!/bin/bash | |
# This script generates a changelog for a given repository between two versions. | |
# It uses the GitHub API to fetch all tags and releases, and then generates a changelog | |
# based on the release notes. | |
# | |
# Use it without installation like so: | |
# | |
# curl -fsSL https://gist.githubusercontent.com/kopach/53168f97cdf8fa91c0998d44f06d15af/raw/gh-release-changelog.sh | bash -s -- \ | |
# --repo kopach/karma-sabarivka-reporter \ | |
# --from v1.1.1 \ | |
# --to v3.0.2 \ | |
# --output karma-changelog-v1.1.1-v3.0.2.md | |
# | |
# Or save it to a file and run it like so: | |
# | |
# bash ./gh-release-changelog.sh --repo kopach/karma-sabarivka-reporter --from v1.1.1 --to v3.0.2 --output karma-changelog-v1.1.1-v3.0.2.md | |
# Initialize | |
REPO="" | |
FROM="" | |
TO="" | |
OUTPUT="" | |
SHOW_HELP=0 | |
# Help text | |
usage() { | |
echo "Usage: $0 [--repo org/repo] [--from v1.0.0] [--to v2.0.0] [--output file.md]" | |
echo "" | |
echo "Options:" | |
echo " --repo GitHub repository (e.g. kopach/karma-sabarivka-reporter)" | |
echo " --from Starting version (older, e.g. v1.1.1)" | |
echo " --to Ending version (newer, e.g. v3.0.2 )" | |
echo " --output Output file (optional; if omitted, prints to stdout)" | |
echo " --help Show this help message" | |
} | |
# Parse args | |
while [[ $# -gt 0 ]]; do | |
case "$1" in | |
--repo) | |
REPO="$2" | |
shift 2 | |
;; | |
--from) | |
FROM="$2" | |
shift 2 | |
;; | |
--to) | |
TO="$2" | |
shift 2 | |
;; | |
--output) | |
OUTPUT="$2" | |
shift 2 | |
;; | |
--help) | |
SHOW_HELP=1 | |
shift | |
;; | |
*) | |
echo "β Unknown option: $1" | |
usage | |
exit 1 | |
;; | |
esac | |
done | |
if [[ $SHOW_HELP -eq 1 ]]; then | |
usage | |
exit 0 | |
fi | |
# Prompt for missing | |
[[ -z "$REPO" ]] && read -p "Enter repository (e.g. actions/setup-node): " REPO | |
[[ -z "$FROM" ]] && read -p "Enter FROM version (older, e.g. v1.2.3): " FROM | |
[[ -z "$TO" ]] && read -p "Enter TO version (newer, e.g. v2.0.0): " TO | |
echo "π Fetching all git tags from $REPO ..." | |
ALL_TAGS=($(gh api repos/$REPO/tags --paginate --jq '.[].name')) | |
# Filter semver tags | |
SEMVER_TAGS=($(printf '%s\n' "${ALL_TAGS[@]}" | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | sort -V)) | |
# Validate FROM/TO | |
if ! printf '%s\n' "${SEMVER_TAGS[@]}" | grep -qx "$FROM"; then | |
echo "β FROM tag '$FROM' not found in semver tag list." | |
exit 1 | |
fi | |
if ! printf '%s\n' "${SEMVER_TAGS[@]}" | grep -qx "$TO"; then | |
echo "β TO tag '$TO' not found in semver tag list." | |
exit 1 | |
fi | |
# Build range: FROM β TO (semver) | |
RANGE_STARTED=0 | |
RANGE_TAGS=() | |
for TAG in "${SEMVER_TAGS[@]}"; do | |
[[ "$TAG" == "$FROM" ]] && RANGE_STARTED=1 | |
[[ $RANGE_STARTED -eq 1 ]] && RANGE_TAGS+=("$TAG") | |
[[ "$TAG" == "$TO" ]] && break | |
done | |
if [[ ${#RANGE_TAGS[@]} -eq 0 ]]; then | |
echo "β No semver tags found between $FROM and $TO." | |
exit 1 | |
fi | |
# Reverse range: TO β FROM | |
REVERSED_TAGS=($(printf '%s\n' "${RANGE_TAGS[@]}" | tail -r)) | |
# Cache release tags | |
RELEASE_MAP=$(gh release list --repo "$REPO" --limit 1000 | awk '{print $1}') | |
# Output setup | |
print() { | |
if [[ -n "$OUTPUT" ]]; then | |
echo -e "$1" >>"$OUTPUT" | |
else | |
echo -e "$1" | |
fi | |
} | |
# Begin output | |
[[ -n "$OUTPUT" ]] && echo "πΎ Writing changelog to $OUTPUT..." | |
print "\nπ Changelog from $TO to $FROM in $REPO:\n" | |
for TAG in "${REVERSED_TAGS[@]}"; do | |
if echo "$RELEASE_MAP" | grep -qx "$TAG"; then | |
print "## $TAG" | |
BODY=$(gh release view "$TAG" --repo "$REPO" --json body -q '.body') | |
print "$BODY" | |
print "\n---\n" | |
fi | |
done | |
[[ -n "$OUTPUT" ]] && echo "β Done: changelog saved to $OUTPUT" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment