forked from ralish/bash-script-template
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuild.sh
More file actions
executable file
·169 lines (142 loc) · 5.1 KB
/
build.sh
File metadata and controls
executable file
·169 lines (142 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#!/usr/bin/env bash
# ============================================================================ #
## FILE : build.sh
## VERSION : 1.0.0
## DESCRIPTION : Merge source and script into template
## AUTHOR : silverbullet069
## REPOSITORY : https://github.com/Silverbullet069/bash-script-template
## LICENSE : BSD-3-Clause
# ============================================================================ #
# DESC: An 'echo' wrapper that redirects standard output to standard error
# ARGS: $@ (required): Message(s) to echo
# OUTS: None
# RETS: None
function log() {
echo "$@" >&2
}
# FUNCTION: check_binary
# DESC: Checks if a given binary/command is available in the system's PATH.
# ARGS: $1 (required): Name of the binary/command to check.
# OUTS: Prints an error message to stderr if the binary is missing.
# RETS: Returns 1 if the binary is missing, 0 otherwise.
function check_binary() {
if ! command -v "$1" >/dev/null 2>&1; then
log "Missing dependency '$1'"
return 1
fi
}
# DESC: Acquire script lock, extracted from script.sh
# ARGS: $1 (required): Scope of script execution lock (system or user)
# OUTS: None
# RETS: None
# NOTE: This lock implementation is extremely simple but should be reliable
# across all platforms. It does *not* support locking a script with
# symlinks or multiple hardlinks as there's no portable way of doing so.
# If the lock was acquired it's automatically released on script exit.
function lock_init() {
local lock_dir
if [[ "${1}" = "system" ]]; then
lock_dir="/tmp/$(basename "${BASH_SOURCE[0]}").lock"
elif [[ "${1}" = "user" ]]; then
lock_dir="/tmp/$(basename "${BASH_SOURCE[0]}").${UID}.lock"
else
log "Missing or invalid argument to ${FUNCNAME[0]}()!"
exit 1
fi
if mkdir "${lock_dir}" 2>/dev/null; then
readonly script_lock="${lock_dir}"
log "Acquired script lock: ${script_lock}"
else
log "Unable to acquire script lock: ${lock_dir}"
exit 2
fi
}
# DESC: Handler for exiting the script
# ARGS: None
# OUTS: None
# RETS: None
function script_trap_exit() {
# Remove script execution lock
if [[ -d "${script_lock-}" ]]; then
rmdir "${script_lock}"
log "Clean up script lock: ${script_lock}"
fi
}
# ============================================================================ #
# Enable xtrace if the DEBUG environment variable is set
if [[ ${DEBUG-} =~ ^1|yes|true$ ]]; then
set -o xtrace # Trace the execution of the script (debug)
fi
# A better class of script...
set -o errexit # Exit on most errors (see the manual)
set -o errtrace # Make sure any error trap is inherited
set -o nounset # Disallow expansion of unset variables
set -o pipefail # Use last non-zero exit code in a pipeline
function build() {
local -r source_path="${1}"
local -r script_path="${2}"
local -r template_path="${3}"
if [[ ! -f "${source_path}" ]]; then
log "source.sh not found: ${script_path}"
exit 1
fi
if [[ ! -r "${source_path}" ]]; then
log "source.sh is unreadable: ${script_path}"
exit 1
fi
if [[ ! -f "${script_path}" ]]; then
log "script.sh not found: ${script_path}"
exit 1
fi
if [[ ! -r "${script_path}" ]]; then
log "script.sh is unreadable: ${script_path}"
exit 1
fi
# NOTE: Update the arbitrary values if header changes
local -r script_header=$(head -n 18 "${script_path}")
local -r source_body=$(tail -n +12 "${source_path}")
local -r script_body=$(tail -n +19 "${script_path}" | grep -vE -e '^# shellcheck source=source.sh$' -e '^# shellcheck disable=SC1091$' -e '^source.*source\.sh"$')
# temporily make it writeable
chmod 755 "${template_path}"
{
log "${script_header}"
log "${source_body}"
log "${script_body}"
} 2>"${template_path}"
# then, make it read-only
chmod 555 "${template_path}"
log "Build ${template_path} successfully."
}
function cleanup() {
log "Stopping file monitor..."
exit 0
}
# Main control flow
function main() {
trap script_trap_exit EXIT
lock_init "user"
local -r source_path="$(dirname "${BASH_SOURCE[0]}")/source.sh"
local -r script_path="$(dirname "${BASH_SOURCE[0]}")/script.sh"
local -r template_path="$(dirname "${BASH_SOURCE[0]}")/template.sh"
# initial build
build "${source_path}" "${script_path}" "${template_path}"
# simple dev server
if [[ "${1-}" =~ ^(--monitor|-m)$ ]]; then
# gracefully stopping dev server
trap cleanup SIGINT SIGTERM SIGHUP
inotifywait \
--monitor \
--event "close_write" \
"${source_path}" "${script_path}" \
| while read -r dir event file; do
log "Change detected: ${event} on ${dir}${file}"
# NOTE: add a small delay to allow accumulation of multiple changes
sleep 1
build "${source_path}" "${script_path}" "${template_path}"
done
fi
}
# Invoke main with args if not sourced
if ! (return 0 2>/dev/null); then
main "$@"
fi