Skip to content

Commit ad3815c

Browse files
authored
✨ feat: Improve log.sh lib (#63)
- Create a `run` function to replace `xscript` in `bin`. - Ditch `LOG_COLOR` and just auto-detect. - Remove `require_cmd`
2 parents 64ca558 + 8d79cd9 commit ad3815c

7 files changed

Lines changed: 109 additions & 65 deletions

File tree

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
SHELL = /bin/bash
1+
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
2+
SHELL = $(ROOT_DIR)/bin/make-shell
23
OS := $(shell bin/distro)
34
PATH := bin:$(PATH)
4-
ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST))))
55

66
.PHONY: clean git macos-brew
77

bin/make-shell

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env bash
2+
# Wrapper used as make SHELL: sources lib/log.sh then runs the recipe.
3+
# So recipes can use run, log_info, etc. without defining RUN in the Makefile.
4+
[ "$1" = -c ] && shift
5+
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
6+
cd "$ROOT" && . ./lib/log.sh && eval "$@"
7+
exit $?

bin/xscript

Lines changed: 0 additions & 8 deletions
This file was deleted.

install.sh

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
77
LOG_LEVEL=10 # DEBUG+
88
LOG_TIME=1
99
LOG_PREFIX="dotfiles-install"
10-
LOG_COLOR=1
1110
# shellcheck source=lib/log.sh
1211
. "$SCRIPT_DIR/lib/log.sh"
1312
# shellcheck source=lib/distro.sh

lib/log.sh

Lines changed: 78 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,25 @@ __LOG_SH_LOADED=1
1313
# LOG_LEVEL=20 # INFO and above
1414
# LOG_TIME=1 # enable timestamps
1515
# LOG_PREFIX="mytool" # prefix tag
16-
# LOG_COLOR=1 # enable ANSI color (best-effort)
1716

1817
: "${LOG_LEVEL:=20}" # 10=DEBUG, 20=INFO, 30=WARN, 40=ERROR
1918
: "${LOG_TIME:=0}" # 0/1
2019
: "${LOG_PREFIX:=}" # e.g. "mytool"
21-
: "${LOG_COLOR:=0}" # 0/1 (best-effort)
2220

2321
# --- Internal helpers (POSIX-safe) -------------------------------------------
2422

2523
_log_level_num() {
2624
# Map a level name to its numeric value.
2725
# Usage: _log_level_num "INFO"
2826
case "$1" in
29-
DEBUG) printf '%s' 10 ;;
30-
INFO) printf '%s' 20 ;;
31-
WARN) printf '%s' 30 ;;
32-
ERROR) printf '%s' 40 ;;
33-
*) printf '%s' 0 ;;
27+
DEBUG) printf '%s' 10 ;;
28+
INFO) printf '%s' 20 ;;
29+
WARN) printf '%s' 30 ;;
30+
ERROR) printf '%s' 40 ;;
31+
DIVIDER) printf '%s' 20 ;;
32+
DIVIDER_END_OK) printf '%s' 20 ;;
33+
DIVIDER_END_ERR) printf '%s' 20 ;;
34+
*) printf '%s' 0 ;;
3435
esac
3536
}
3637

@@ -42,9 +43,9 @@ _log_now() {
4243
}
4344

4445
_log_use_color() {
45-
# Best-effort: use color only when LOG_COLOR=1, stderr/stdout are TTY, and not in CI/non‑TTY/dumb/no_color.
46+
# Best-effort: use color when stderr is TTY and not in CI/dumb/no_color.
4647
# `-t` is POSIX for test in many shells; if unsupported, this will just fail false-ish.
47-
[ "${LOG_COLOR}" = "1" ] && [ -t 1 ] && [ -z "${NO_COLOR-}" ] && [ -n "${TERM-}" ] && [ "${TERM-}" != "dumb" ]
48+
[ -t 2 ] && [ -z "${NO_COLOR-}" ] && [ -n "${TERM-}" ] && [ "${TERM-}" != "dumb" ]
4849
}
4950

5051
_log_fmt_prefix() {
@@ -85,11 +86,14 @@ _log_print() {
8586
# Colour (best-effort, stderr only)
8687
if _log_use_color; then
8788
case "$lvl" in
88-
DEBUG) c_start="$(printf '🐛 \033[36m')" ;; # cyan
89-
INFO) c_start="$(printf 'ℹ️ \033[32m')" ;; # green
90-
WARN) c_start="$(printf '⚠️ \033[33m')" ;; # yellow
91-
ERROR) c_start="$(printf '🛑 \033[31m')" ;; # red
92-
*) c_start="" ;;
89+
DEBUG) c_start="$(printf '🐛 \033[36m')" ;; # cyan
90+
INFO) c_start="$(printf 'ℹ️ \033[32m')" ;; # green
91+
WARN) c_start="$(printf '⚠️ \033[33m')" ;; # yellow
92+
ERROR) c_start="$(printf '🛑 \033[31m')" ;; # red
93+
DIVIDER) c_start="$(printf '\033[32m')" ;; # green (⌛️ in title)
94+
DIVIDER_END_OK) c_start="$(printf '\033[32m')" ;; # green (✅ in title)
95+
DIVIDER_END_ERR) c_start="$(printf '\033[31m')" ;; # red (🛑 in title)
96+
*) c_start="" ;;
9397
esac
9498
c_end="$(printf '\033[0m')"
9599
else
@@ -99,22 +103,80 @@ _log_print() {
99103

100104
# Print to stderr (so logs don’t pollute stdout pipelines)
101105
# Shellcheck note: we intentionally want "$*" to preserve spaces as one message.
102-
prefix="$(_log_fmt_prefix "$lvl")"
106+
case "$lvl" in
107+
DIVIDER|DIVIDER_END_OK|DIVIDER_END_ERR) prefix="" ;;
108+
*) prefix="$(_log_fmt_prefix "$lvl")" ;;
109+
esac
103110
# If message is empty, still print prefix.
104111
if [ "$#" -gt 0 ]; then
105-
printf '%s%s %s%s\n' "$c_start" "$prefix" "$*" "$c_end" >&2
112+
if [ -n "$prefix" ]; then
113+
printf '%s%s %s%s\n' "$c_start" "$prefix" "$*" "$c_end" >&2
114+
else
115+
printf '%s%s%s\n' "$c_start" "$*" "$c_end" >&2
116+
fi
106117
else
107118
printf '%s%s%s\n' "$c_start" "$prefix" "$c_end" >&2
108119
fi
109120
}
110121

122+
_log_terminal_width() {
123+
# Best-effort: COLUMNS, then tput cols, then stty size, else 80.
124+
w="${COLUMNS:-}"
125+
if [ -z "$w" ]; then
126+
w="$(tput cols 2>/dev/null)" || w=""
127+
fi
128+
if [ -z "$w" ]; then
129+
w="$(stty size 2>/dev/null | awk '{print $2}')" || w=""
130+
fi
131+
case "$w" in
132+
''|*[!0-9]*) printf '%s' 80 ;;
133+
*) printf '%s' "$w" ;;
134+
esac
135+
}
136+
137+
_log_divider() {
138+
# Arg 1: begin | end. Arg 2: script path. Arg 3 (end only): exit code (0 = success → green ✅, else red 🛑).
139+
kind="$1"
140+
path="$2"
141+
exit_code="${3:-0}"
142+
case "$kind" in
143+
begin) title="⌛️ BEGIN $path"; lvl="DIVIDER" ;;
144+
end) if [ "$exit_code" = "0" ]; then title="✅ END $path"; lvl="DIVIDER_END_OK"; else title="🛑 END $path"; lvl="DIVIDER_END_ERR"; fi ;;
145+
*) title="$path"; lvl="DIVIDER" ;;
146+
esac
147+
w="$(_log_terminal_width)"
148+
len=$(printf '%s' " $title " | wc -c | tr -d ' ')
149+
left_n=$(( (w - len) / 2 ))
150+
right_n=$(( w - len - left_n ))
151+
[ "$left_n" -lt 0 ] && left_n=0
152+
[ "$right_n" -lt 0 ] && right_n=0
153+
left=""
154+
i=0; while [ "$i" -lt "$left_n" ]; do left="${left}="; i=$((i+1)); done
155+
right=""
156+
i=0; while [ "$i" -lt "$right_n" ]; do right="${right}="; i=$((i+1)); done
157+
_log_print "$lvl" "$left $title $right"
158+
}
159+
111160
# --- Public API --------------------------------------------------------------
112161

113162
log_debug() { _log_print DEBUG "$*"; }
114163
log_info() { _log_print INFO "$*"; }
115164
log_warn() { _log_print WARN "$*"; }
116165
log_error() { _log_print ERROR "$*"; }
117166

167+
run() {
168+
# run "path/to/script"
169+
# Execute a script with pre-configured log (LOG_* exported; script runs in current shell and can use log_*).
170+
[ -z "$1" ] && { log_error "run: missing script path"; return 1; }
171+
[ ! -r "$1" ] && { log_error "run: not readable or not found: $1"; return 1; }
172+
export LOG_LEVEL LOG_TIME LOG_PREFIX
173+
_log_divider begin "$1"
174+
( . "$1" ) # Run in subshell
175+
ret=$?
176+
_log_divider end "$1" "$ret"
177+
return $ret
178+
}
179+
118180
die() {
119181
# die "message" [exit_code]
120182
# Returns non-zero if sourced; if executed in a subshell, exit is up to the caller.
@@ -123,19 +185,3 @@ die() {
123185
log_error "$msg"
124186
return "$code"
125187
}
126-
127-
# Convenience: assert command exists
128-
require_cmd() {
129-
# require_cmd curl git jq
130-
# Returns non-zero (and logs) if any command is missing.
131-
missing=""
132-
for cmd in "$@"; do
133-
command -v "$cmd" >/dev/null 2>&1 || missing="${missing} ${cmd}"
134-
done
135-
136-
if [ -n "$missing" ]; then
137-
log_error "missing required command(s):${missing}"
138-
return 127
139-
fi
140-
return 0
141-
}

makefiles/macos.mk

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
macos-core: macos-brew macos-stow macos-zsh
66

77
macos-brew:
8-
xscript "pkg/brew/macos-brew.sh"
8+
run "pkg/brew/macos-brew.sh"
99

1010
macos-clean:
1111
find . -name ".DS_Store" -delete
@@ -45,52 +45,52 @@ macos-cli-useful: macos-brew
4545

4646
macos-alfred: macos-brew
4747
brew bundle --file brewfiles/alfred.Brewfile
48-
xscript "scripts/macos-alfred.sh"
48+
run "scripts/macos-alfred.sh"
4949

5050
macos-appcleaner: macos-brew
5151
brew install --cask appcleaner
5252
defaults import net.freemacsoft.AppCleaner pkg/macos-appcleaner/net.freemacsoft.AppCleaner.plist
5353

5454
macos-cleanshot: macos-brew
5555
brew bundle --file brewfiles/cleanshot.Brewfile
56-
xscript "scripts/macos-cleanshot.sh"
56+
run "scripts/macos-cleanshot.sh"
5757

5858
macos-finder:
59-
xscript "scripts/macos-finder.sh"
59+
run "scripts/macos-finder.sh"
6060

6161
macos-hammerspoon: macos-brew macos-stow
6262
brew install --cask hammerspoon
6363
stow --no-folding --dir 'pkg' --target "${HOME}" 'macos-hammerspoon'
6464

6565
macos-iterm: macos-brew
6666
brew bundle --file pkg/macos-iterm/macos-iterm.Brewfile
67-
exists imgcat || xscript "pkg/macos-iterm/macos-iterm.sh"
67+
exists imgcat || (run "pkg/macos-iterm/macos-iterm.sh")
6868

6969
macos-markedit: macos-brew
7070
brew bundle --file brewfiles/markedit.Brewfile
71-
xscript "scripts/macos-markedit.sh"
71+
run "scripts/macos-markedit.sh"
7272

7373
macos-one-password: macos-brew
7474
brew bundle --file brewfiles/one-password.Brewfile
7575

7676
macos-popclip: macos-brew
7777
brew bundle --file brewfiles/popclip.Brewfile
78-
xscript "scripts/macos-popclip.sh"
78+
run "scripts/macos-popclip.sh"
7979

8080
macos-sublime: macos-brew macos-stow
8181
brew install --cask sublime-text
82-
xscript "pkg/sublime/macos-sublime.sh"
82+
run "pkg/sublime/macos-sublime.sh"
8383

8484
macos-terminal: macos-brew
8585
brew bundle --file brewfiles/terminal.Brewfile
8686

8787
macos-tower: macos-brew macos-git
8888
brew install --cask tower
89-
xscript "pkg/macos-tower/macos-tower.sh"
89+
run "pkg/macos-tower/macos-tower.sh"
9090

9191
macos-vscode: macos-brew
9292
brew install --cask visual-studio-code
93-
xscript "pkg/vscode/macos-vscode.sh"
93+
run "pkg/vscode/macos-vscode.sh"
9494

9595
macos-xcode: macos-brew
9696
brew bundle --file brewfiles/xcode.Brewfile
@@ -126,7 +126,7 @@ macos-stow: macos-brew macos-clean
126126
macos-zsh: macos-brew macos-stow
127127
$(eval BREW_BIN := $(shell bin/brew_bin))
128128
${BREW_BIN}/brew bundle --file brewfiles/zsh.Brewfile
129-
xscript "scripts/macos-zsh.sh"
129+
run "scripts/macos-zsh.sh"
130130

131131
###############################################################################
132132
# Dev #
@@ -140,21 +140,21 @@ macos-javascript:
140140

141141
macos-perl: macos-stow
142142
stow --no-folding --dir 'pkg/perl' --target "${HOME}" 'stow'
143-
xscript "pkg/perl/macos-perl.sh"
143+
run "pkg/perl/macos-perl.sh"
144144

145145
###############################################################################
146146
# Misc #
147147
###############################################################################
148148

149149
macos-file-handler: macos-duti
150-
xscript "scripts/macos-file-handler.sh"
150+
run "scripts/macos-file-handler.sh"
151151

152152
macos-font: macos-brew
153153
brew bundle --file pkg/macos-font/macos-font.Brewfile
154154
cp -r pkg/macos-font/collection/ "${HOME}/Library/FontCollections"
155155

156156
macos-icon: macos-cli-useful
157-
xscript "scripts/macos-icons.sh"
157+
run "scripts/macos-icons.sh"
158158

159159
macos-quicklook: macos-brew
160160
brew bundle --file brewfiles/ext-quicklook.Brewfile
@@ -166,7 +166,7 @@ macos-service-workflow: macos-stow
166166
stow --dir 'pkg' --target "${HOME}/Library/Services" 'macos-services'
167167

168168
macos-settings:
169-
xscript "scripts/macos-settings.sh"
169+
run "scripts/macos-settings.sh"
170170

171171
macos-touch-id-sudo:
172-
xscript "scripts/macos-touch-id-sudo.sh"
172+
run "scripts/macos-touch-id-sudo.sh"

makefiles/ubuntu.mk

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ ubuntu-cli-network:
3030
###############################################################################
3131

3232
ubuntu-dropbox: ubuntu-essential
33-
xscript "scripts/dropbox.sh"
33+
run "scripts/dropbox.sh"
3434

3535
###############################################################################
3636
# CLIs #
@@ -40,7 +40,7 @@ ubuntu-git: ubuntu-stow
4040
xargs sudo apt-get install -y < pkg/git/git.Aptfile
4141

4242
ubuntu-micro: ubuntu-stow
43-
xscript "pkg/micro/debian-micro.sh"
43+
run "pkg/micro/debian-micro.sh"
4444

4545
ubuntu-nano:
4646
sudo apt-get update
@@ -51,7 +51,7 @@ ubuntu-stow:
5151
sudo apt-get install -y stow
5252

5353
ubuntu-zsh: ubuntu-stow
54-
xscript "scripts/debian-zsh.sh"
54+
run "scripts/debian-zsh.sh"
5555

5656
###############################################################################
5757
# Dev #
@@ -65,10 +65,10 @@ ubuntu-javascript:
6565
###############################################################################
6666

6767
ubuntu-locale-zhtw:
68-
xscript "scripts/debian-locale-zhtw.sh"
68+
run "scripts/debian-locale-zhtw.sh"
6969

7070
ubuntu-ssh:
71-
xscript "scripts/debian-ssh.sh"
71+
run "scripts/debian-ssh.sh"
7272

7373
ubuntu-tz-taipei:
74-
xscript "scripts/debian-tz-taipei.sh"
74+
run "scripts/debian-tz-taipei.sh"

0 commit comments

Comments
 (0)