auto-toolkit 0.1.0b1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,366 @@
1
+ #!/usr/bin/env bash
2
+ # Declarative argument parsing and usage generator for automation scripts.
3
+ #
4
+ # Example Usage:
5
+ # source "$LIB_DIR/common.sh"
6
+ # source "$LIB_DIR/args.sh"
7
+ #
8
+ # cli_init "auto install auto-toolkit [OPTIONS]" \
9
+ # "Install and symlink auto CLI binary into your PATH."
10
+ #
11
+ # cli_opt "--path" "-p" "Specify target installation directory" TARGET_DIR "$HOME/.local/bin"
12
+ # cli_flag "--dry-run" "-d" "Simulate installation steps" DRY_RUN
13
+ # cli_flag "--force" "-f" "Force overwrite of existing files" FORCE
14
+ #
15
+ # cli_example "auto install auto-toolkit"
16
+ # cli_example "auto install auto-toolkit --dry-run"
17
+ #
18
+ # cli_parse "$@"
19
+
20
+ # Global CLI definitions
21
+ CLI_USAGE=""
22
+ CLI_DESCRIPTION=""
23
+ CLI_OPTS=()
24
+ CLI_EXAMPLES=()
25
+ CLI_POSITIONAL=()
26
+
27
+ # Initialize CLI metadata
28
+ cli_init() {
29
+ CLI_USAGE="$1"
30
+ CLI_DESCRIPTION="${2:-}"
31
+ CLI_OPTS=()
32
+ CLI_EXAMPLES=()
33
+ CLI_POSITIONAL=()
34
+
35
+ # Automatically add built-in help flag
36
+ cli_flag "--help" "-h" "Show this help message and exit" CLI_SHOW_HELP false
37
+ }
38
+
39
+ # Register a boolean flag
40
+ # Syntax: cli_flag <long> <short> <description> <var_name> [default_val=false]
41
+ cli_flag() {
42
+ local long="${1#--}"
43
+ local short="${2#-}"
44
+ local desc="$3"
45
+ local var_name="$4"
46
+ local default_val="${5:-false}"
47
+
48
+ # Set default value in caller's environment
49
+ printf -v "$var_name" "%s" "$default_val"
50
+
51
+ CLI_OPTS+=("flag|${long}|${short}|${var_name}|${default_val}|${desc}")
52
+ }
53
+
54
+ # Register an option taking a value
55
+ # Syntax: cli_opt <long> <short> <description> <var_name> [default_val=""] [required=false]
56
+ cli_opt() {
57
+ local long="${1#--}"
58
+ local short="${2#-}"
59
+ local desc="$3"
60
+ local var_name="$4"
61
+ local default_val="${5:-}"
62
+ local required="${6:-false}"
63
+
64
+ # Set default value in caller's environment
65
+ printf -v "$var_name" "%s" "$default_val"
66
+
67
+ CLI_OPTS+=("val|${long}|${short}|${var_name}|${default_val}|${desc}|${required}")
68
+ }
69
+
70
+ # Add a usage example
71
+ cli_example() {
72
+ CLI_EXAMPLES+=("$1")
73
+ }
74
+
75
+ # Print formatted help text
76
+ cli_help() {
77
+ local c_bold="${COLOR_BOLD:-\033[1m}"
78
+ local c_cyan="${COLOR_CYAN:-\033[36m}"
79
+ local c_dim="${COLOR_DIM:-\033[90m}"
80
+ local c_reset="${COLOR_RESET:-\033[0m}"
81
+
82
+ echo -e "${c_bold}Usage:${c_reset} ${CLI_USAGE}"
83
+
84
+ if [[ -n "$CLI_DESCRIPTION" ]]; then
85
+ echo ""
86
+ echo -e " ${CLI_DESCRIPTION}"
87
+ fi
88
+
89
+ if [[ ${#CLI_OPTS[@]} -gt 0 ]]; then
90
+ echo ""
91
+ echo -e "${c_bold}Options:${c_reset}"
92
+
93
+ # Calculate max flag width for clean alignment
94
+ local max_flag_len=0
95
+ local entry
96
+
97
+ for entry in "${CLI_OPTS[@]}"; do
98
+ IFS='|' read -r opt_type opt_long opt_short opt_var opt_default opt_desc opt_req <<< "$entry"
99
+ local flag_display=""
100
+
101
+ if [[ -n "$opt_short" ]]; then
102
+ flag_display="-${opt_short}, --${opt_long}"
103
+ else
104
+ flag_display=" --${opt_long}"
105
+ fi
106
+
107
+ if [[ "$opt_type" == "val" ]]; then
108
+ flag_display="${flag_display} <val>"
109
+ fi
110
+
111
+ if [[ ${#flag_display} -gt $max_flag_len ]]; then
112
+ max_flag_len=${#flag_display}
113
+ fi
114
+ done
115
+
116
+ local col_width=$((max_flag_len + 4))
117
+
118
+ for entry in "${CLI_OPTS[@]}"; do
119
+ IFS='|' read -r opt_type opt_long opt_short opt_var opt_default opt_desc opt_req <<< "$entry"
120
+ local flag_display=""
121
+
122
+ if [[ -n "$opt_short" ]]; then
123
+ flag_display="-${opt_short}, --${opt_long}"
124
+ else
125
+ flag_display=" --${opt_long}"
126
+ fi
127
+
128
+ if [[ "$opt_type" == "val" ]]; then
129
+ flag_display="${flag_display} <val>"
130
+ fi
131
+
132
+ local default_info=""
133
+
134
+ if [[ -n "$opt_default" && "$opt_default" != "false" ]]; then
135
+ default_info=" ${c_dim}(default: ${opt_default})${c_reset}"
136
+ fi
137
+
138
+ if [[ "$opt_req" == "true" ]]; then
139
+ default_info=" ${c_cyan}(required)${c_reset}"
140
+ fi
141
+
142
+ printf " %-*s %s%b\n" "$col_width" "$flag_display" "$opt_desc" "$default_info"
143
+ done
144
+ fi
145
+
146
+ if [[ ${#CLI_EXAMPLES[@]} -gt 0 ]]; then
147
+ echo ""
148
+ echo -e "${c_bold}Examples:${c_reset}"
149
+
150
+ local ex
151
+
152
+ for ex in "${CLI_EXAMPLES[@]}"; do
153
+ echo -e " $ex"
154
+ done
155
+ fi
156
+
157
+ echo ""
158
+ }
159
+
160
+ # Parse command-line arguments according to registered options
161
+ cli_parse() {
162
+ local args=("$@")
163
+ CLI_POSITIONAL=()
164
+
165
+ while [[ ${#args[@]} -gt 0 ]]; do
166
+ local key="${args[0]}"
167
+
168
+ case "$key" in
169
+ -h|--help)
170
+ cli_help
171
+ exit 0
172
+ ;;
173
+
174
+ --)
175
+ shift
176
+ CLI_POSITIONAL+=("${args[@]}")
177
+ break
178
+ ;;
179
+
180
+ --*=*)
181
+ # Handle --option=value syntax
182
+ local opt_name="${key%%=*}"
183
+ opt_name="${opt_name#--}"
184
+ local opt_val="${key#*=}"
185
+ local matched=false
186
+ local entry
187
+
188
+ for entry in "${CLI_OPTS[@]}"; do
189
+ IFS='|' read -r opt_type opt_long opt_short opt_var opt_default opt_desc opt_req <<< "$entry"
190
+
191
+ if [[ "$opt_long" == "$opt_name" && "$opt_type" == "val" ]]; then
192
+ printf -v "$opt_var" "%s" "$opt_val"
193
+ matched=true
194
+ break
195
+ fi
196
+ done
197
+
198
+ if [[ "$matched" == false ]]; then
199
+ if command -v log_error &>/dev/null; then
200
+ log_error "Unknown option: $key"
201
+ else
202
+ echo -e "Error: Unknown option: $key" >&2
203
+ fi
204
+ cli_help >&2
205
+ exit 1
206
+ fi
207
+
208
+ args=("${args[@]:1}")
209
+ ;;
210
+
211
+ --*)
212
+ local opt_name="${key#--}"
213
+ local matched=false
214
+ local entry
215
+
216
+ for entry in "${CLI_OPTS[@]}"; do
217
+ IFS='|' read -r opt_type opt_long opt_short opt_var opt_default opt_desc opt_req <<< "$entry"
218
+
219
+ if [[ "$opt_long" == "$opt_name" ]]; then
220
+ if [[ "$opt_type" == "flag" ]]; then
221
+ printf -v "$opt_var" "%s" "true"
222
+ matched=true
223
+ args=("${args[@]:1}")
224
+ break
225
+ elif [[ "$opt_type" == "val" ]]; then
226
+ if [[ ${#args[@]} -lt 2 || "${args[1]}" =~ ^- ]]; then
227
+ if command -v log_error &>/dev/null; then
228
+ log_error "Option '--$opt_long' requires a value."
229
+ else
230
+ echo -e "Error: Option '--$opt_long' requires a value." >&2
231
+ fi
232
+ cli_help >&2
233
+ exit 1
234
+ fi
235
+
236
+ printf -v "$opt_var" "%s" "${args[1]}"
237
+ matched=true
238
+ args=("${args[@]:2}")
239
+ break
240
+ fi
241
+ fi
242
+ done
243
+
244
+ if [[ "$matched" == false ]]; then
245
+ if command -v log_error &>/dev/null; then
246
+ log_error "Unknown option: $key"
247
+ else
248
+ echo -e "Error: Unknown option: $key" >&2
249
+ fi
250
+ cli_help >&2
251
+ exit 1
252
+ fi
253
+ ;;
254
+
255
+ -[a-zA-Z0-9]*)
256
+ local opt_name="${key#-}"
257
+ local matched=false
258
+
259
+ # 1. Check for combined single-character short flags (e.g. -fd, -xzf)
260
+ if [[ ${#opt_name} -gt 1 ]]; then
261
+ local all_flags=true
262
+ local matched_vars=()
263
+ local i
264
+
265
+ for (( i=0; i<${#opt_name}; i++ )); do
266
+ local ch="${opt_name:i:1}"
267
+ local ch_found=false
268
+ local entry
269
+
270
+ for entry in "${CLI_OPTS[@]}"; do
271
+ IFS='|' read -r opt_type opt_long opt_short opt_var opt_default opt_desc opt_req <<< "$entry"
272
+
273
+ if [[ "$opt_short" == "$ch" && "$opt_type" == "flag" ]]; then
274
+ matched_vars+=("$opt_var")
275
+ ch_found=true
276
+ break
277
+ fi
278
+ done
279
+
280
+ if [[ "$ch_found" == false ]]; then
281
+ all_flags=false
282
+ break
283
+ fi
284
+ done
285
+
286
+ if [[ "$all_flags" == true && ${#matched_vars[@]} -gt 0 ]]; then
287
+ for v in "${matched_vars[@]}"; do
288
+ printf -v "$v" "%s" "true"
289
+ done
290
+
291
+ args=("${args[@]:1}")
292
+ continue
293
+ fi
294
+ fi
295
+
296
+ # 2. Check if it matches a registered short option or flag
297
+ local entry
298
+
299
+ for entry in "${CLI_OPTS[@]}"; do
300
+ IFS='|' read -r opt_type opt_long opt_short opt_var opt_default opt_desc opt_req <<< "$entry"
301
+
302
+ if [[ "$opt_short" == "$opt_name" ]]; then
303
+ if [[ "$opt_type" == "flag" ]]; then
304
+ printf -v "$opt_var" "%s" "true"
305
+ matched=true
306
+ args=("${args[@]:1}")
307
+ break
308
+ elif [[ "$opt_type" == "val" ]]; then
309
+ if [[ ${#args[@]} -lt 2 || "${args[1]}" =~ ^- ]]; then
310
+ if command -v log_error &>/dev/null; then
311
+ log_error "Option '-$opt_short' requires a value."
312
+ else
313
+ echo -e "Error: Option '-$opt_short' requires a value." >&2
314
+ fi
315
+ cli_help >&2
316
+ exit 1
317
+ fi
318
+
319
+ printf -v "$opt_var" "%s" "${args[1]}"
320
+ matched=true
321
+ args=("${args[@]:2}")
322
+ break
323
+ fi
324
+ fi
325
+ done
326
+
327
+ if [[ "$matched" == false ]]; then
328
+ if command -v log_error &>/dev/null; then
329
+ log_error "Unknown option: $key"
330
+ else
331
+ echo -e "Error: Unknown option: $key" >&2
332
+ fi
333
+ cli_help >&2
334
+ exit 1
335
+ fi
336
+ ;;
337
+
338
+ *)
339
+ CLI_POSITIONAL+=("$key")
340
+ args=("${args[@]:1}")
341
+ ;;
342
+ esac
343
+ done
344
+
345
+ # Validate required options
346
+ local entry
347
+
348
+ for entry in "${CLI_OPTS[@]}"; do
349
+ IFS='|' read -r opt_type opt_long opt_short opt_var opt_default opt_desc opt_req <<< "$entry"
350
+
351
+ if [[ "$opt_req" == "true" ]]; then
352
+ local current_val="${!opt_var}"
353
+
354
+ if [[ -z "$current_val" ]]; then
355
+ if command -v log_error &>/dev/null; then
356
+ log_error "Missing required option: --${opt_long}"
357
+ else
358
+ echo -e "Error: Missing required option: --${opt_long}" >&2
359
+ fi
360
+
361
+ cli_help >&2
362
+ exit 1
363
+ fi
364
+ fi
365
+ done
366
+ }
@@ -0,0 +1,122 @@
1
+ #!/usr/bin/env bash
2
+ # Common helper functions for logging, terminal colors, and temporary directory lifecycle
3
+
4
+ # Color initialization (respects NO_COLOR, non-interactive terminals, and dumb terminal)
5
+ init_colors() {
6
+ if [[ -z "${NO_COLOR:-}" && -t 1 && "${TERM:-}" != "dumb" ]]; then
7
+ COLOR_RESET="\033[0m"
8
+ COLOR_BOLD="\033[1m"
9
+ COLOR_RED="\033[31m"
10
+ COLOR_GREEN="\033[32m"
11
+ COLOR_YELLOW="\033[33m"
12
+ COLOR_BLUE="\033[34m"
13
+ COLOR_MAGENTA="\033[35m"
14
+ COLOR_CYAN="\033[36m"
15
+ COLOR_DIM="\033[90m"
16
+ else
17
+ COLOR_RESET=""
18
+ COLOR_BOLD=""
19
+ COLOR_RED=""
20
+ COLOR_GREEN=""
21
+ COLOR_YELLOW=""
22
+ COLOR_BLUE=""
23
+ COLOR_MAGENTA=""
24
+ COLOR_CYAN=""
25
+ COLOR_DIM=""
26
+ fi
27
+ }
28
+
29
+ init_colors
30
+
31
+ log_info() {
32
+ echo -e "${COLOR_CYAN}==>${COLOR_RESET} ${COLOR_BOLD}$*${COLOR_RESET}"
33
+ }
34
+
35
+ log_step() {
36
+ echo -e " ${COLOR_BLUE}->${COLOR_RESET} $*"
37
+ }
38
+
39
+ log_success() {
40
+ echo -e " ${COLOR_GREEN}[✓]${COLOR_RESET} $*"
41
+ }
42
+
43
+ log_warn() {
44
+ echo -e " ${COLOR_YELLOW}[!] Warning:${COLOR_RESET} $*" >&2
45
+ }
46
+
47
+ log_error() {
48
+ echo -e " ${COLOR_RED}[✗] Error:${COLOR_RESET} $*" >&2
49
+ }
50
+
51
+ # Temporary directory tracking & cleanup
52
+ declare -a _AUTO_TEMP_DIRS=()
53
+
54
+ _cleanup_temp_dirs() {
55
+ local dir
56
+
57
+ for dir in "${_AUTO_TEMP_DIRS[@]}"; do
58
+ if [[ -d "$dir" ]]; then
59
+ rm -rf "$dir" 2>/dev/null || true
60
+ fi
61
+ done
62
+ }
63
+
64
+ trap '_cleanup_temp_dirs' EXIT INT TERM
65
+
66
+ create_temp_dir() {
67
+ local prefix="${1:-auto-toolkit}"
68
+ local tmp_dir
69
+
70
+ tmp_dir="$(mktemp -d "/tmp/${prefix}.XXXXXX")"
71
+ _AUTO_TEMP_DIRS+=("$tmp_dir")
72
+
73
+ echo "$tmp_dir"
74
+ }
75
+
76
+ setup_temp_dir() {
77
+ local prefix="${1:-auto-toolkit}"
78
+
79
+ create_temp_dir "$prefix"
80
+ }
81
+
82
+ # Verify required binaries exist on host PATH
83
+ require_commands() {
84
+ local missing=()
85
+ local cmd
86
+
87
+ for cmd in "$@"; do
88
+ if ! command -v "$cmd" &>/dev/null; then
89
+ missing+=("$cmd")
90
+ fi
91
+ done
92
+
93
+ if [[ ${#missing[@]} -gt 0 ]]; then
94
+ log_error "Missing required commands: ${missing[*]}"
95
+ log_error "Please install the missing tools and try again."
96
+ exit 1
97
+ fi
98
+
99
+ return 0
100
+ }
101
+
102
+ # Interactive confirmation prompt with default handling
103
+ confirm_action() {
104
+ local prompt="${1:-Are you sure?}"
105
+ local default_ans="${2:-N}"
106
+
107
+ if [[ "${FORCE:-false}" == "true" ]]; then
108
+ return 0
109
+ fi
110
+
111
+ if [[ ! -t 0 ]]; then
112
+ [[ "${default_ans^^}" == "Y" ]] && return 0 || return 1
113
+ fi
114
+
115
+ local prompt_suffix="[y/N]"
116
+ [[ "${default_ans^^}" == "Y" ]] && prompt_suffix="[Y/n]"
117
+
118
+ read -r -p "$prompt $prompt_suffix " answer
119
+ answer="${answer:-$default_ans}"
120
+
121
+ [[ "${answer,,}" =~ ^y(es)?$ ]]
122
+ }
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env bash
2
+ # Robust downloading utilities with retry logic, timeout, and progress feedback
3
+
4
+ # Download a file from URL to output destination using curl or wget
5
+ # Arguments:
6
+ # $1: Source URL (http://, https://, file://)
7
+ # $2: Destination file path
8
+ # $3: Retry attempts (default: 3)
9
+ # $4: Connection timeout in seconds (default: 15)
10
+ fetch_file() {
11
+ local url="$1"
12
+ local dest="$2"
13
+ local retries="${3:-3}"
14
+ local timeout="${4:-15}"
15
+
16
+ if [[ -z "$url" || -z "$dest" ]]; then
17
+ echo "Usage: fetch_file <url> <dest> [retries] [timeout]" >&2
18
+ return 1
19
+ fi
20
+
21
+ local dest_dir
22
+ dest_dir="$(dirname "$dest")"
23
+ mkdir -p "$dest_dir"
24
+
25
+ # Handle local file:// protocol
26
+ if [[ "$url" =~ ^file:// ]]; then
27
+ local local_path="${url#file://}"
28
+
29
+ if [[ -f "$local_path" ]]; then
30
+ cp -f "$local_path" "$dest"
31
+ return 0
32
+ else
33
+ echo "Error: Local source file not found: $local_path" >&2
34
+ return 1
35
+ fi
36
+ fi
37
+
38
+ if command -v curl &>/dev/null; then
39
+ curl --fail \
40
+ --location \
41
+ --connect-timeout "$timeout" \
42
+ --retry "$retries" \
43
+ --retry-connrefused \
44
+ --silent \
45
+ --show-error \
46
+ -o "$dest" \
47
+ "$url"
48
+ elif command -v wget &>/dev/null; then
49
+ wget --tries="$retries" \
50
+ --timeout="$timeout" \
51
+ --quiet \
52
+ --show-progress \
53
+ -O "$dest" \
54
+ "$url"
55
+ else
56
+ echo "Error: Neither 'curl' nor 'wget' is available on this system." >&2
57
+ return 1
58
+ fi
59
+ }
60
+
61
+ # Fetch text content (e.g. version file, signature, key) to standard output
62
+ # Arguments:
63
+ # $1: Source URL
64
+ # $2: Connection timeout in seconds (default: 10)
65
+ fetch_text() {
66
+ local url="$1"
67
+ local timeout="${2:-10}"
68
+
69
+ if [[ -z "$url" ]]; then
70
+ echo "Usage: fetch_text <url> [timeout]" >&2
71
+ return 1
72
+ fi
73
+
74
+ # Handle local file:// protocol
75
+ if [[ "$url" =~ ^file:// ]]; then
76
+ local local_path="${url#file://}"
77
+
78
+ if [[ -f "$local_path" ]]; then
79
+ cat "$local_path"
80
+ return 0
81
+ else
82
+ echo "Error: Local source file not found: $local_path" >&2
83
+ return 1
84
+ fi
85
+ fi
86
+
87
+ if command -v curl &>/dev/null; then
88
+ curl --fail \
89
+ --location \
90
+ --connect-timeout "$timeout" \
91
+ --silent \
92
+ "$url"
93
+ elif command -v wget &>/dev/null; then
94
+ wget --timeout="$timeout" \
95
+ --quiet \
96
+ -O - \
97
+ "$url"
98
+ else
99
+ echo "Error: Neither 'curl' nor 'wget' is available on this system." >&2
100
+ return 1
101
+ fi
102
+ }