Created
June 14, 2025 19:48
-
-
Save robbiemu/c31f1324f8b2480c64530e492ff5fc34 to your computer and use it in GitHub Desktop.
zip_project - Zip up source and documentation files from a Git repo, excluding .git, .venv, and anything in .gitignore.
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 | |
# zip_project.sh - Zip up source and documentation files from a Git repo, | |
# excluding .git, .venv, and anything in .gitignore. Use --help for options. | |
# | |
# Usage: | |
# ./zip_project.sh [--output FILE] [--verbose] [--filter [REGEX]] [--exclude REGEX] [--help] | |
# | |
# Options: | |
# -o, --output FILE Name of the output zip file (default: project_bundle.zip) | |
# -v, --verbose Print verbose output | |
# -f, --filter [REGEX] Only include files matching REGEX. If REGEX is omitted, | |
# default to code + markdown extensions. | |
# -e, --exclude REGEX Exclude files matching REGEX from the archive | |
# -h, --help Show this help message and exit | |
ZIPFILE="project_bundle.zip" | |
VERBOSE=0 | |
FILTER=0 | |
FILTER_PATTERN='' | |
EXCLUDE_PATTERN='' | |
DEFAULT_FILTER='\.(py|js|ts|go|java|rb|rs|cpp|c|md|sh|json|yml|yaml|toml|ipynb)$' | |
print_help_short() { | |
tail -n +2 "$0" | sed -n 's/^# \{0,1\}//p' | sed '/^$/q' | |
} | |
print_help() { | |
awk ' | |
NR==1 && /^#!/ { next } | |
/^#/ { sub(/^# ?/, ""); print; next } | |
{ exit } | |
' "$0" | |
} | |
log() { | |
if [ "$VERBOSE" = "1" ]; then | |
echo "[zip_project] $1" | |
fi | |
} | |
# Manual argument parser | |
while [ "$#" -gt 0 ]; do | |
case "$1" in | |
-o|--output) | |
ZIPFILE="$2" | |
shift 2 | |
;; | |
-v|--verbose) | |
VERBOSE=1 | |
shift | |
;; | |
-f|--filter) | |
FILTER=1 | |
shift | |
if [ "$#" -gt 0 ] && echo "$1" | grep -v '^-' > /dev/null; then | |
FILTER_PATTERN="$1" | |
shift | |
fi | |
;; | |
-e|--exclude) | |
shift | |
if [ "$#" -gt 0 ] && ! echo "$1" | grep -q '^-' ; then | |
EXCLUDE_PATTERN="$1" | |
shift | |
else | |
echo "Error: --exclude requires a regex pattern argument" >&2 | |
exit 1 | |
fi | |
;; | |
-h|--help) | |
print_help | |
exit 0 | |
;; | |
*) | |
echo "Unknown option: $1" >&2 | |
print_help_short | |
exit 1 | |
;; | |
esac | |
done | |
log "Output zip will be: $ZIPFILE" | |
TMPDIR=$(mktemp -d "/tmp/zip_project.XXXXXX") || exit 1 | |
log "Using temporary directory: $TMPDIR" | |
git ls-files --cached --others --exclude-standard > "$TMPDIR/all_files.txt" | |
if [ "$FILTER" = "1" ]; then | |
if [ -z "$FILTER_PATTERN" ]; then | |
FILTER_PATTERN="$DEFAULT_FILTER" | |
log "Filtering with default pattern: $FILTER_PATTERN" | |
else | |
log "Filtering with custom pattern: $FILTER_PATTERN" | |
fi | |
if [ -n "$EXCLUDE_PATTERN" ]; then | |
log "Excluding files matching: $EXCLUDE_PATTERN" | |
egrep "$FILTER_PATTERN" "$TMPDIR/all_files.txt" | grep -v -E "$EXCLUDE_PATTERN" > "$TMPDIR/filelist.txt" | |
else | |
egrep "$FILTER_PATTERN" "$TMPDIR/all_files.txt" > "$TMPDIR/filelist.txt" | |
fi | |
else | |
if [ -n "$EXCLUDE_PATTERN" ]; then | |
log "Excluding files matching: $EXCLUDE_PATTERN" | |
grep -v -E "$EXCLUDE_PATTERN" "$TMPDIR/all_files.txt" > "$TMPDIR/filelist.txt" | |
else | |
cp "$TMPDIR/all_files.txt" "$TMPDIR/filelist.txt" | |
fi | |
fi | |
log "Copying files to staging..." | |
mkdir -p "$TMPDIR/staging" | |
while read file; do | |
dir=$(dirname "$file") | |
mkdir -p "$TMPDIR/staging/$dir" | |
cp "$file" "$TMPDIR/staging/$file" | |
done < "$TMPDIR/filelist.txt" | |
log "Creating zip archive..." | |
OUTDIR=$(pwd) | |
(cd "$TMPDIR/staging" && zip -9rq "$OUTDIR/$ZIPFILE" .) | |
rm -rf "$TMPDIR" | |
echo "✅ Created zip archive: $ZIPFILE" | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment