Compare commits

...

3 commits

4 changed files with 297 additions and 141 deletions

View file

@ -102,3 +102,4 @@
gpgsign = true gpgsign = true
[init] [init]
defaultBranch = main defaultBranch = main
templatedir = ~/.config/git/templates

View file

@ -0,0 +1,156 @@
#!/usr/bin/env bash
set -euo pipefail
# A hook script to check the commit log message.
# Called by "git commit" with one argument, the name of the file
# that has the commit message. The hook should exit with non-zero
# status after issuing an appropriate message if it wants to stop the
# commit. The hook is allowed to edit the commit message file.
# This script focuses on enforcing semantic commit message specification.
#
# It primarily focuses on the commit title, however, it also enforces some
# the mandatory newline between the title and the body.
LIGHT_GRAY="\033[0;37m"
YELLOW="\033[33m"
CYAN="\033[36m"
RED="\033[31m"
UNDO_COLOR="\033[0m"
check_dependencies() {
if ! [ -x "$(command -v jq)" ]; then
echo "\`commit-msg\` hook failed. Please install jq."
exit 1
fi
}
load_config() {
config_file="$PWD/.commit-msg-config.json"
if [ ! -f "$config_file" ]; then
exit 0
fi
enabled=$(jq -r .enabled "$config_file")
if [ "$enabled" != "true" ]; then
exit 0
fi
title_semver_enabled=$(jq -r .semver_structure "$config_file")
mapfile -t special_types < <(jq -r '.special_types[]' "$config_file")
mapfile -t types < <(jq -r '.types[]' "$config_file")
mapfile -t scopes < <(jq -r '.scopes[]' "$config_file")
title_min_length=$(jq -r '.length.min' "$config_file")
title_max_length=$(jq -r '.length.max' "$config_file")
}
# Build a dynamic regex for the commit message
build_title_regex() {
regexp="^("
# Special types
if [ ${#special_types[@]} -eq 0 ]; then
for s_type in "${special_types[@]}"; do
regexp="${regexp}$s_type|"
done
regexp="${regexp%|})"
regexp="${regexp}(: .+)?"
regexp="${regexp}$|^("
fi
# Types
if [ ${#types[@]} -eq 0 ]; then
regexp="${regexp}.+"
else
for type in "${types[@]}"; do
regexp="${regexp}$type|"
done
regexp="${regexp%|})"
fi
# Scope
regexp="${regexp}(\("
if [ ${#scopes[@]} -eq 0 ]; then
regexp="${regexp}.+"
else
for scope in "${scopes[@]}"; do
regexp="${regexp}$scope|"
done
regexp="${regexp%|}"
fi
regexp="${regexp}\))?"
# Breaking change indicator
regexp="${regexp}(!)?"
regexp="${regexp}: (.+)$"
}
error_header() {
echo -e "${RED}[Invalid Commit Message]${UNDO_COLOR}"
echo -e "------------------------"
}
# ---------------------------------------------
INPUT_FILE="$1"
check_dependencies
load_config
# Get the actual commit message, excluding comments
commit_message=$(sed '/^#/d' <"$INPUT_FILE")
# Don't do any checking on empty commit messages.
# Git will abort empty commits by default anyways.
if [ -z "$commit_message" ]; then
exit 0
fi
commit_title=$(echo "$commit_message" | head -n1 "$INPUT_FILE")
# Check the semver commit title structure first
if [ "$title_semver_enabled" == "true" ]; then
build_title_regex
if [[ ! $commit_title =~ $regexp ]]; then
error_header
echo -e "${LIGHT_GRAY}Valid types: ${UNDO_COLOR}${CYAN}${types[*]}${UNDO_COLOR}"
echo -e "${LIGHT_GRAY}Valid special types: ${UNDO_COLOR}${CYAN}${special_types[*]}${UNDO_COLOR}"
if [ ${#scopes[@]} -eq 0 ]; then
echo -e "${LIGHT_GRAY}Valid scopes: any${UNDO_COLOR}"
else
echo -e "${LIGHT_GRAY}Valid scopes: ${UNDO_COLOR}${CYAN}${scopes[*]}${UNDO_COLOR}"
fi
#echo -e "${LIGHT_GRAY}Expected regex: ${UNDO_COLOR}${CYAN}${regexp}${UNDO_COLOR}"
echo -e "Actual commit title: ${YELLOW}${commit_title}${UNDO_COLOR}"
exit 1
fi
fi
# Then check the length of the commit title
commit_title_len=${#commit_title}
if { [ "$title_min_length" != "null" ] && [ "$commit_title_len" -lt "$title_min_length" ]; } ||
{ [ "$title_max_length" != "null" ] && [ "$commit_title_len" -gt "$title_max_length" ]; }; then
error_header
echo -e "${LIGHT_GRAY}Expected title (first line) length: Min=${CYAN}$title_min_length${UNDO_COLOR} Max=${CYAN}$title_max_length${UNDO_COLOR}"
echo -e "Actual length: ${YELLOW}${commit_title_len}${UNDO_COLOR}"
exit 1
fi
# Check for a newline between the title and the body
if [ "$(echo "$commit_message" | wc -l)" -gt 1 ]; then
second_line=$(echo "$commit_message" | sed -n '2p')
third_line=$(echo "$commit_message" | sed -n '3p')
if [ "$second_line" != "" ] || [ "$third_line" == "" ]; then
error_header
echo -e "There must be exactly one blank line between the commit title and the body."
exit 1
fi
fi

View file

@ -1,19 +1,18 @@
#!/bin/sh #!/bin/bash
# Inspired by grimblast (https://github.com/hyprwm/contrib/blob/main/grimblast/grimblast) # Inspired by grimblast (https://github.com/hyprwm/contrib/blob/main/grimblast/grimblast)
# Helper functions # Helper functions
die() { die() {
MSG="${1}" MSG="${1}"
ERR_CODE="${2:-1}" ERR_CODE="${2:-1}"
URGENCY="${3:-critical}" URGENCY="${3:-critical}"
>&2 echo "$MSG" if [ "$NOTIFY" = "yes" ]; then
if [ "$NOTIFY" = "yes" ]; then notify-send -a screenshot -u "$URGENCY" "Error ($ERR_CODE)" "$MSG"
notify-send -a screenshot -u "$URGENCY" "Error ($ERR_CODE)" "$MSG" fi
fi exit "$ERR_CODE"
exit "$ERR_CODE"
} }
# Argument parsing # Argument parsing
@ -27,9 +26,9 @@ EDIT=no
DELAY=0 DELAY=0
while [ "${1-}" ]; do while [ "${1-}" ]; do
case "$1" in case "$1" in
-h | --help) -h | --help)
>&2 cat <<EOF >&2 cat <<EOF
screenshot taking utility script, allowing for easy all-in-one solution for screenshot taking utility script, allowing for easy all-in-one solution for
controlling how a screenshot should be taken. controlling how a screenshot should be taken.
@ -54,163 +53,163 @@ Variables:
- all: Everything (all visible monitors/outputs) - all: Everything (all visible monitors/outputs)
- area: Manually select a region - area: Manually select a region
EOF EOF
exit 0 exit 0
;; ;;
--notify) --notify)
NOTIFY=yes NOTIFY=yes
shift shift
;; ;;
--cursor) --cursor)
CURSOR=yes CURSOR=yes
shift shift
;; ;;
--edit) --edit)
EDIT=yes EDIT=yes
shift shift
;; ;;
--target) --target)
if [ -z "$TARGET" ]; then if [ -z "$TARGET" ]; then
case "$2" in case "$2" in
activewin | window | activemon | monitor | all | area) activewin | window | activemon | monitor | all | area)
TARGET="$2" TARGET="$2"
shift 2 shift 2
;; ;;
*) *)
die "Invalid target (see TARGET variable in --help)" die "Invalid target (see TARGET variable in --help)"
;; ;;
esac esac
else else
die "Only one target can be passed." die "Only one target can be passed."
fi fi
;; ;;
--delay) --delay)
case "$2" in case "$2" in
'' | *[!0-9]*) '' | *[!0-9]*)
die "Argument after --delay must be an amount of MILISECONDS" die "Argument after --delay must be an amount of MILISECONDS"
;; ;;
*) *)
DELAY="$2" DELAY="$2"
shift 2 shift 2
;; ;;
esac esac
;; ;;
--copy) --copy)
if [ -z "$SAVE_METHOD" ]; then if [ -z "$SAVE_METHOD" ]; then
SAVE_METHOD="copy" SAVE_METHOD="copy"
shift shift
else else
die "Only one method can be passed." die "Only one method can be passed."
fi fi
;; ;;
--save) --save)
if [ -z "$SAVE_METHOD" ]; then if [ -z "$SAVE_METHOD" ]; then
SAVE_METHOD="save" SAVE_METHOD="save"
SAVE_FILE="$2" SAVE_FILE="$2"
shift 2 shift 2
else else
die "Only one method can be passed." die "Only one method can be passed."
fi fi
;; ;;
--copysave) --copysave)
if [ -z "$SAVE_METHOD" ]; then if [ -z "$SAVE_METHOD" ]; then
SAVE_METHOD="copysave" SAVE_METHOD="copysave"
SAVE_FILE="$2" SAVE_FILE="$2"
shift 2 shift 2
else else
die "Only one method can be passed." die "Only one method can be passed."
fi fi
;; ;;
*) *)
die "Unrecognized argument: $1" die "Unrecognized argument: $1"
;; ;;
esac esac
done done
# Screenshot functions # Screenshot functions
takeScreenshot() { takeScreenshot() {
FILE="$1" FILE="$1"
GEOM="$2" GEOM="$2"
ARGS=() ARGS=()
[ "$CURSOR" = "yes" ] && ARGS+=("-c") [ "$CURSOR" = "yes" ] && ARGS+=("-c")
[ -n "$GEOM" ] && ARGS+=("-g" "$GEOM") [ -n "$GEOM" ] && ARGS+=("-g" "$GEOM")
ARGS+=("$FILE") ARGS+=("$FILE")
sleep "$DELAY"e-3 sleep "$DELAY"e-3
grim "${ARGS[@]}" || die "Unable to invoke grim" grim "${ARGS[@]}" || die "Unable to invoke grim"
} }
takeEditedScreenshot() { takeEditedScreenshot() {
FILE="$1" FILE="$1"
GEOM="$2" GEOM="$2"
if [ "$EDIT" = "yes" ]; then if [ "$EDIT" = "yes" ]; then
takeScreenshot - "$GEOM" | swappy -f - -o "$FILE" || die "Unable to invoke swappy" takeScreenshot - "$GEOM" | swappy -f - -o "$FILE" || die "Unable to invoke swappy"
else else
takeScreenshot "$FILE" "$GEOM" takeScreenshot "$FILE" "$GEOM"
fi fi
} }
# Obtain the geometry for screenshot to be taken at # Obtain the geometry for screenshot to be taken at
if [ "$TARGET" = "area" ]; then if [ "$TARGET" = "area" ]; then
GEOM="$(slurp -d)" GEOM="$(slurp -d)"
if [ -z "$GEOM" ]; then if [ -z "$GEOM" ]; then
die "No area selected" 2 normal die "No area selected" 2 normal
fi fi
WHAT="Area" WHAT="Area"
elif [ "$TARGET" = "all" ]; then elif [ "$TARGET" = "all" ]; then
GEOM="" GEOM=""
WHAT="Screen" WHAT="Screen"
elif [ "$TARGET" = "activewin" ]; then elif [ "$TARGET" = "activewin" ]; then
FOCUSED="$(hyprctl activewindow -j)" FOCUSED="$(hyprctl activewindow -j)"
GEOM="$(echo "$FOCUSED" | jq -r '"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"')" GEOM="$(echo "$FOCUSED" | jq -r '"\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"')"
APP_ID="$(echo "$FOCUSED" | jq -r '.class')" APP_ID="$(echo "$FOCUSED" | jq -r '.class')"
WHAT="$APP_ID window" WHAT="$APP_ID window"
elif [ "$TARGET" = "window" ]; then elif [ "$TARGET" = "window" ]; then
WORKSPACES="$(hyprctl monitors -j | jq -r 'map(.activeWorkspace.id)')" WORKSPACES="$(hyprctl monitors -j | jq -r 'map(.activeWorkspace.id)')"
WINDOWS="$(hyprctl clients -j | jq -r --argjson workspaces "$WORKSPACES" 'map(select([.workspace.id] | inside($workspaces)))')" WINDOWS="$(hyprctl clients -j | jq -r --argjson workspaces "$WORKSPACES" 'map(select([.workspace.id] | inside($workspaces)))')"
GEOM=$(echo "$WINDOWS" | jq -r '.[] | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp -r) GEOM=$(echo "$WINDOWS" | jq -r '.[] | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp -r)
if [ -z "$GEOM" ]; then if [ -z "$GEOM" ]; then
die "No window selected" 2 normal die "No window selected" 2 normal
fi fi
WHAT="Window" WHAT="Window"
elif [ "$TARGET" = "activemon" ]; then elif [ "$TARGET" = "activemon" ]; then
ACTIVEMON="$(hyprctl monitors -j | jq -r '.[] | select(.focused == true)')" ACTIVEMON="$(hyprctl monitors -j | jq -r '.[] | select(.focused == true)')"
GEOM="$(echo "$ACTIVEMON" | jq -r '"\(.x),\(.y) \(.width)x\(.height)"')" GEOM="$(echo "$ACTIVEMON" | jq -r '"\(.x),\(.y) \(.width)x\(.height)"')"
WHAT="$(echo "$ACTIVEMON" | jq -r '.name')" WHAT="$(echo "$ACTIVEMON" | jq -r '.name')"
elif [ "$TARGET" = "monitor" ]; then elif [ "$TARGET" = "monitor" ]; then
GEOM="$(slurp -o)" GEOM="$(slurp -o)"
if [ -z "$GEOM" ]; then if [ -z "$GEOM" ]; then
die "No monitor selected" 2 normal die "No monitor selected" 2 normal
fi fi
WHAT="Monitor" WHAT="Monitor"
else else
if [ -z "$TARGET" ]; then if [ -z "$TARGET" ]; then
die "No target specified!" die "No target specified!"
else else
die "Unknown target: $SAVE_METHOD" die "Unknown target: $SAVE_METHOD"
fi fi
fi fi
# Invoke grim and capture the screenshot # Invoke grim and capture the screenshot
if [ "$SAVE_METHOD" = "save" ]; then if [ "$SAVE_METHOD" = "save" ]; then
takeEditedScreenshot "$SAVE_FILE" "$GEOM" takeEditedScreenshot "$SAVE_FILE" "$GEOM"
[ "$NOTIFY" = "yes" ] && notify-send -a screenshot "Success" "$WHAT screenshot saved" -i "$(realpath "$SAVE_FILE")" [ "$NOTIFY" = "yes" ] && notify-send -a screenshot "Success" "$WHAT screenshot saved" -i "$(realpath "$SAVE_FILE")"
elif [ "$SAVE_METHOD" = "copy" ]; then elif [ "$SAVE_METHOD" = "copy" ]; then
TEMP_FILE="$(mktemp --suffix=.png)" TEMP_FILE="$(mktemp --suffix=.png)"
takeEditedScreenshot "-" "$GEOM" | tee "$TEMP_FILE" | wl-copy --type image/png || die "Clipboard error" takeEditedScreenshot "-" "$GEOM" | tee "$TEMP_FILE" | wl-copy --type image/png || die "Clipboard error"
[ "$NOTIFY" = "yes" ] && notify-send -a screenshot "Success" "$WHAT screenshot copied" -i "$(realpath "$TEMP_FILE")" && rm "$TEMP_FILE" [ "$NOTIFY" = "yes" ] && notify-send -a screenshot "Success" "$WHAT screenshot copied" -i "$(realpath "$TEMP_FILE")" && rm "$TEMP_FILE"
elif [ "$SAVE_METHOD" = "copysave" ]; then elif [ "$SAVE_METHOD" = "copysave" ]; then
takeEditedScreenshot "-" "$GEOM" | tee "$SAVE_FILE" | wl-copy --type image/png || die "Clipboard error" takeEditedScreenshot "-" "$GEOM" | tee "$SAVE_FILE" | wl-copy --type image/png || die "Clipboard error"
[ "$NOTIFY" = "yes" ] && notify-send -a screenshot "Success" "$WHAT screenshot copied and saved" -i "$(realpath "$SAVE_FILE")" [ "$NOTIFY" = "yes" ] && notify-send -a screenshot "Success" "$WHAT screenshot copied and saved" -i "$(realpath "$SAVE_FILE")"
else else
if [ -z "$SAVE_METHOD" ]; then if [ -z "$SAVE_METHOD" ]; then
die "No save method specified!" die "No save method specified!"
else else
die "Unknown save method: $SAVE_METHOD" die "Unknown save method: $SAVE_METHOD"
fi fi
fi fi

View file

@ -107,7 +107,7 @@ paru -S --noconfirm --needed \
# Wayland Utilities # Wayland Utilities
paru -S --noconfirm --needed \ paru -S --noconfirm --needed \
grim slurp wofi swappy-git wf-recorder wlogout clipman hyprpicker hyprpaper grim slurp wofi swappy wf-recorder wlogout clipman hyprpicker hyprpaper
# Applications # Applications
paru -S --noconfirm --needed \ paru -S --noconfirm --needed \