#!/usr/bin/env bash ################################################################################### # This script is run by git before a commit is made. # # To use it, copy it to .git/hooks/pre-commit and make it executable. # # Alternatively, run the following command from the root of the repo: # # git config core.hooksPath .githooks # # # # FEATURES # # Updates the "updated" field in the front matter of .md files. # # Compresses PNG files with either oxipng or optipng if available. # # Runs subset_font if config.toml has been modified. # # Aborts the commit if a draft .md file or a file with "TODO" is being committed. # # Updates the README if the line numbers for the language section have changed. # ################################################################################### # Function to exit the script with an error message. function error_exit() { echo "ERROR: $1" >&2 exit "${2:-1}" } # Function to extract the date from the front matter. function extract_date() { local file="$1" local field="$2" grep -m 1 "^$field =" "$file" | sed -e "s/$field = //" -e 's/ *$//' } # Function to check if the .md file is a draft. function is_draft() { local file="$1" awk '/^\+\+\+$/,/^\+\+\+$/ { if(/draft = true/) exit 0 } END { exit 1 }' "$file" } # Check if the file contains "TODO". function contains_todo() { local file="$1" grep -q "TODO" "$file" } # Check if the script is being run from the root of the repo. if [[ ! $(git rev-parse --show-toplevel) == $(pwd) ]]; then error_exit "This script must be run from the root of the repo." fi # Check if oxipng is installed. png_compressor="" if command -v oxipng &> /dev/null; then png_compressor="oxipng -o max" elif command -v optipng &> /dev/null; then png_compressor="optipng -o 7" fi ################################################################## # Compress PNG files with either oxipng or optipng if available. # # Update the "updated" field in the front matter of .md files. # # https://osc.garden/blog/zola-date-git-hook/ # # Interrupt the commit if a draft .md file is being committed. # ################################################################## # Get the newly added and modified files. all_changed_files=$(git diff --cached --name-only --diff-filter=AM) # Loop through each newly added or modified .md or .png file for PNG optimization and draft checking. for file in $all_changed_files; do # If the file is a PNG and png_compressor is set, compress it and add it to the commit. if [[ "$file" == *.png ]] && [[ -n "$png_compressor" ]]; then $png_compressor "$file" || error_exit "Failed to compress PNG file $file" git add --force "$file" || error_exit "Failed to add compressed PNG file $file" continue fi # If the file is an .md file and it's a draft, abort the commit. if [[ "$file" == *.md ]]; then if is_draft "$file"; then error_exit "Draft file $file is being committed!" fi fi # If the file contains "TODO", abort the commit. if contains_todo "$file"; then error_exit "File $file contains TODO! Remove or complete the TODO before committing." fi done # Get the modified .md to update the "updated" field in the front matter. modified_files=$(git diff --cached --name-only --diff-filter=M | grep -E '\.md$' | grep -v '_index.md$') # Loop through each modified .md file. for file in $modified_files; do # Get the last modified date from the filesystem. last_modified_date=$(date -r "$file" +'%Y-%m-%d') # Extract the "date" field from the front matter. date_value=$(extract_date "$file" "date") # Skip the file if the last modified date is the same as the "date" field. if [[ "$last_modified_date" == "$date_value" ]]; then continue fi # Update the "updated" field with the last modified date. # If the "updated" field doesn't exist, create it below the "date" field. if ! awk -v date_line="$last_modified_date" 'BEGIN{FS=OFS=" = "; first = 1} { if (/^date =/ && first) { print; getline; if (!/^updated =/) print "updated" OFS date_line; first=0; } if (/^updated =/ && !first) gsub(/[^ ]*$/, date_line, $2); print; }' "$file" > "${file}.tmp" then error_exit "Failed to process $file with AWK" fi mv "${file}.tmp" "$file" || error_exit "Failed to overwrite $file with updated content" # Stage the changes. git add "$file" done ######################################################### # Run subset_font if config.toml has been modified. # # https://welpo.github.io/tabi/blog/custom-font-subset/ # ######################################################### if git diff --cached --name-only | grep -q "config.toml"; then echo "config.toml modified. Running subset_font…" # Call the subset_font script. ~/bin/subset_font -c config.toml -f static/fonts/Inter4.woff2 -o static/ # Add the generated subset.css file to the commit. git add static/custom_subset.css fi ################################################################################ # Update the README if the line numbers for the language section have changed. # ################################################################################ # File paths and names. config_file="config.toml" config_readme="README.md" # Ensure the required files are present. [ ! -f "$config_file" ] && error_exit "$config_file not found!" [ ! -f "$config_readme" ] && error_exit "$config_readme not found!" # Determine the line numbers for relevant sections in config.toml. lang_start_line=$(grep -n "^\[languages.es\]$" "$config_file" | cut -d: -f1) extra_start_line=$(grep -n "^\[extra\]$" "$config_file" | cut -d: -f1) lang_end_line=$((extra_start_line - 2)) # Extract currently documented line numbers from README. documented_lines=$(grep -o 'https://github.com/welpo/tabi/blob/main/config.toml#L[0-9]*-L[0-9]*' "$config_readme" | grep -o 'L[0-9]*-L[0-9]*') doc_start_line=$(echo "$documented_lines" | cut -d'-' -f1 | tr -d 'L') doc_end_line=$(echo "$documented_lines" | cut -d'-' -f2 | tr -d 'L') # Ensure that the variables are set and are numbers. if [[ ! $lang_start_line =~ ^[0-9]+$ ]] || [[ ! $doc_start_line =~ ^[0-9]+$ ]] || [[ ! $lang_end_line =~ ^[0-9]+$ ]] || [[ ! $doc_end_line =~ ^[0-9]+$ ]]; then error_exit "Line number variables are not set correctly." fi # Update the README if there's a discrepancy in the line numbers. if [ "$lang_start_line" -ne "$doc_start_line" ] || [ "$lang_end_line" -ne "$doc_end_line" ]; then if ! perl -pi -e "s|https://github.com/welpo/tabi/blob/main/config.toml#L[0-9]*-L[0-9]*|https://github.com/welpo/tabi/blob/main/config.toml#L$lang_start_line-L$lang_end_line|g" "$config_readme"; then error_exit "Perl processing failed for $config_readme" fi # Add updated README to commit. git add "$config_readme" fi