configure-precommit 1.0.0

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.
package/lib/builder.sh ADDED
@@ -0,0 +1,311 @@
1
+ #!/usr/bin/env bash
2
+ # Phase 3: Builder
3
+ # Installs pre-commit and configures hooks
4
+
5
+ set -euo pipefail
6
+
7
+ # Colors for output
8
+ RED='\033[0;31m'
9
+ GREEN='\033[0;32m'
10
+ YELLOW='\033[1;33m'
11
+ NC='\033[0m' # No Color
12
+
13
+ # Logging functions (can be overridden by main script)
14
+ # Write to stderr to avoid polluting stdout
15
+ log_info() {
16
+ echo -e "${GREEN}[INFO]${NC} $1" >&2
17
+ }
18
+
19
+ log_warn() {
20
+ echo -e "${YELLOW}[WARN]${NC} $1" >&2
21
+ }
22
+
23
+ log_error() {
24
+ echo -e "${RED}[ERROR]${NC} $1" >&2
25
+ }
26
+
27
+ # Check if pre-commit is installed
28
+ is_precommit_installed() {
29
+ command -v pre-commit &>/dev/null
30
+ }
31
+
32
+ # Check if agentform is installed
33
+ is_agentform_installed() {
34
+ command -v agentform &>/dev/null
35
+ }
36
+
37
+ # Install agentform-cli
38
+ install_agentform() {
39
+ if is_agentform_installed; then
40
+ log_info "agentform is already installed: $(command -v agentform)"
41
+ return 0
42
+ fi
43
+
44
+ log_info "Installing agentform-cli..."
45
+
46
+ # Try pipx first (preferred for CLI tools)
47
+ if command -v pipx &>/dev/null; then
48
+ log_info "Using pipx to install agentform-cli"
49
+ if pipx install agentform-cli 2>/dev/null; then
50
+ export PATH="$HOME/.local/bin:$PATH"
51
+ return 0
52
+ fi
53
+ log_warn "pipx install failed, trying pip..."
54
+ fi
55
+
56
+ # Try pip in the precommit venv if it exists
57
+ local venv_dir="${PRECOMMIT_VENV_DIR:-$HOME/.precommit-venv}"
58
+ if [[ -d "$venv_dir" ]]; then
59
+ log_info "Installing agentform-cli in existing venv"
60
+ if "$venv_dir/bin/pip" install agentform-cli 2>/dev/null; then
61
+ return 0
62
+ fi
63
+ fi
64
+
65
+ # Try pip3 --user
66
+ if command -v pip3 &>/dev/null; then
67
+ log_info "Using pip3 --user to install agentform-cli"
68
+ if pip3 install --user agentform-cli 2>/dev/null; then
69
+ export PATH="$HOME/.local/bin:$PATH"
70
+ return 0
71
+ fi
72
+ fi
73
+
74
+ log_warn "Could not install agentform-cli. Will use direct API calls as fallback."
75
+ return 1
76
+ }
77
+
78
+ # Create a virtual environment for pre-commit
79
+ create_precommit_venv() {
80
+ local venv_dir="${PRECOMMIT_VENV_DIR:-$HOME/.precommit-venv}"
81
+
82
+ log_info "Creating virtual environment at $venv_dir..."
83
+
84
+ # Find Python 3
85
+ local python_cmd=""
86
+ if command -v python3 &>/dev/null; then
87
+ python_cmd="python3"
88
+ elif command -v python &>/dev/null; then
89
+ python_cmd="python"
90
+ else
91
+ log_error "Python not found"
92
+ return 1
93
+ fi
94
+
95
+ # Create venv
96
+ if ! $python_cmd -m venv "$venv_dir"; then
97
+ log_error "Failed to create virtual environment"
98
+ return 1
99
+ fi
100
+
101
+ # Install pre-commit in venv
102
+ if ! "$venv_dir/bin/pip" install pre-commit; then
103
+ log_error "Failed to install pre-commit in virtual environment"
104
+ return 1
105
+ fi
106
+
107
+ # Add to PATH for this session
108
+ export PATH="$venv_dir/bin:$PATH"
109
+
110
+ log_info "Virtual environment created successfully"
111
+ log_info "Add this to your shell profile: export PATH=\"$venv_dir/bin:\$PATH\""
112
+
113
+ return 0
114
+ }
115
+
116
+ # Install pre-commit using available package manager
117
+ install_precommit() {
118
+ if is_precommit_installed; then
119
+ log_info "pre-commit is already installed: $(command -v pre-commit)"
120
+ return 0
121
+ fi
122
+
123
+ log_info "Installing pre-commit..."
124
+
125
+ # Try pipx first (preferred for CLI tools)
126
+ if command -v pipx &>/dev/null; then
127
+ log_info "Trying pipx..."
128
+ if pipx install pre-commit 2>/dev/null; then
129
+ log_info "Installed via pipx"
130
+ # pipx installs to ~/.local/bin
131
+ export PATH="$HOME/.local/bin:$PATH"
132
+ return 0
133
+ fi
134
+ log_warn "pipx install failed, trying next method..."
135
+ fi
136
+
137
+ # Try homebrew on macOS (before pip to avoid PEP 668 issues)
138
+ if command -v brew &>/dev/null; then
139
+ log_info "Trying Homebrew..."
140
+ if brew install pre-commit 2>/dev/null; then
141
+ log_info "Installed via Homebrew"
142
+ return 0
143
+ fi
144
+ log_warn "Homebrew install failed, trying next method..."
145
+ fi
146
+
147
+ # Try pip with --user flag (may fail on PEP 668 systems)
148
+ if command -v pip3 &>/dev/null; then
149
+ log_info "Trying pip3 --user..."
150
+ if pip3 install --user pre-commit 2>/dev/null; then
151
+ log_info "Installed via pip3"
152
+ export PATH="$HOME/.local/bin:$PATH"
153
+ return 0
154
+ fi
155
+ log_warn "pip3 install failed (likely PEP 668), trying next method..."
156
+ fi
157
+
158
+ # Try apt on Debian/Ubuntu
159
+ if command -v apt-get &>/dev/null; then
160
+ log_info "Trying apt..."
161
+ if sudo apt-get update && sudo apt-get install -y pre-commit 2>/dev/null; then
162
+ log_info "Installed via apt"
163
+ return 0
164
+ fi
165
+ log_warn "apt install failed, trying next method..."
166
+ fi
167
+
168
+ # Fallback: Create a virtual environment
169
+ log_info "Falling back to virtual environment..."
170
+ if create_precommit_venv; then
171
+ return 0
172
+ fi
173
+
174
+ log_error "Cannot install pre-commit. Please install it manually:"
175
+ log_error " brew install pre-commit"
176
+ log_error " OR: pipx install pre-commit"
177
+ log_error " OR: pip install --user pre-commit (if not PEP 668 restricted)"
178
+ return 1
179
+ }
180
+
181
+ # Write the pre-commit config to file
182
+ write_config() {
183
+ local config_content="$1"
184
+ local config_path="${2:-.pre-commit-config.yaml}"
185
+
186
+ echo "$config_content" > "$config_path"
187
+ log_info "Wrote configuration to $config_path"
188
+ }
189
+
190
+ # Update hook versions to latest
191
+ update_versions() {
192
+ local skip_autoupdate="${PRECOMMIT_SKIP_AUTOUPDATE:-false}"
193
+
194
+ if [[ "$skip_autoupdate" == "true" ]]; then
195
+ log_warn "Skipping version auto-update (PRECOMMIT_SKIP_AUTOUPDATE=true)"
196
+ return 0
197
+ fi
198
+
199
+ log_info "Updating hook versions to latest..."
200
+
201
+ # Replace AUTOUPDATE placeholder with a valid placeholder first
202
+ # pre-commit autoupdate needs valid semver tags to work
203
+ if grep -q "rev: AUTOUPDATE" .pre-commit-config.yaml 2>/dev/null; then
204
+ # Use a recent known version as starting point
205
+ sed -i.bak 's/rev: AUTOUPDATE/rev: v0.0.1/g' .pre-commit-config.yaml 2>/dev/null || \
206
+ sed -i '' 's/rev: AUTOUPDATE/rev: v0.0.1/g' .pre-commit-config.yaml
207
+ rm -f .pre-commit-config.yaml.bak
208
+ fi
209
+
210
+ if ! pre-commit autoupdate; then
211
+ log_warn "Some hooks failed to update. Continuing with available versions."
212
+ fi
213
+ }
214
+
215
+ # Install git hooks
216
+ install_git_hooks() {
217
+ log_info "Installing git hooks..."
218
+
219
+ if ! pre-commit install; then
220
+ log_error "Failed to install git hooks"
221
+ return 1
222
+ fi
223
+
224
+ log_info "Git hooks installed successfully"
225
+ }
226
+
227
+ # Pre-fetch hook environments
228
+ prefetch_hooks() {
229
+ log_info "Pre-fetching hook environments (this may take a moment)..."
230
+
231
+ if ! pre-commit install-hooks; then
232
+ log_warn "Some hooks failed to install. They will be installed on first run."
233
+ fi
234
+
235
+ log_info "Hook environments ready"
236
+ }
237
+
238
+ # Main builder function
239
+ run_builder() {
240
+ local config_content="$1"
241
+ local repo_root="${2:-.}"
242
+ local dry_run="${PRECOMMIT_DRY_RUN:-false}"
243
+
244
+ cd "$repo_root"
245
+
246
+ # Write config
247
+ write_config "$config_content"
248
+
249
+ if [[ "$dry_run" == "true" ]]; then
250
+ log_warn "Dry run mode - skipping installation"
251
+ return 0
252
+ fi
253
+
254
+ # Install pre-commit if needed
255
+ if ! install_precommit; then
256
+ return 1
257
+ fi
258
+
259
+ # Ensure pre-commit is in PATH after installation
260
+ if ! is_precommit_installed; then
261
+ # Try to find it in common locations
262
+ local possible_paths=(
263
+ "$HOME/.local/bin/pre-commit"
264
+ "$HOME/.precommit-venv/bin/pre-commit"
265
+ "$HOME/Library/Python/3.*/bin/pre-commit"
266
+ "/usr/local/bin/pre-commit"
267
+ "/opt/homebrew/bin/pre-commit"
268
+ )
269
+ for path_pattern in "${possible_paths[@]}"; do
270
+ for path in $path_pattern; do
271
+ if [[ -x "$path" ]]; then
272
+ local bin_dir
273
+ bin_dir="$(dirname "$path")"
274
+ export PATH="$bin_dir:$PATH"
275
+ log_info "Added $bin_dir to PATH"
276
+ break 2
277
+ fi
278
+ done
279
+ done
280
+ fi
281
+
282
+ # Verify installation
283
+ if ! is_precommit_installed; then
284
+ log_error "pre-commit installed but not found in PATH"
285
+ log_error "You may need to restart your shell or add ~/.local/bin to PATH"
286
+ return 1
287
+ fi
288
+
289
+ # Update versions
290
+ update_versions
291
+
292
+ # Install hooks
293
+ install_git_hooks
294
+
295
+ # Pre-fetch environments
296
+ prefetch_hooks
297
+
298
+ log_info "Builder phase complete"
299
+ return 0
300
+ }
301
+
302
+ # If run directly (not sourced)
303
+ if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
304
+ if [[ $# -lt 1 ]]; then
305
+ echo "Usage: $0 <config-content> [repo-root]"
306
+ echo " config-content: The YAML content for .pre-commit-config.yaml"
307
+ echo " repo-root: Repository root directory (default: current directory)"
308
+ exit 1
309
+ fi
310
+ run_builder "$@"
311
+ fi
package/lib/logging.sh ADDED
@@ -0,0 +1,201 @@
1
+ #!/usr/bin/env bash
2
+ # Logging utilities for pre-commit setup
3
+ # Logs to both console and file
4
+
5
+ # Initialize logging
6
+ # Creates a timestamped log file in /tmp/precommit-setup/
7
+ init_logging() {
8
+ local repo_name="${1:-unknown}"
9
+
10
+ # Create log directory
11
+ LOG_DIR="/tmp/precommit-setup"
12
+ mkdir -p "$LOG_DIR"
13
+
14
+ # Create timestamped log file
15
+ local timestamp
16
+ timestamp=$(date +"%Y%m%d_%H%M%S")
17
+ LOG_FILE="$LOG_DIR/${repo_name}_${timestamp}.log"
18
+
19
+ # Initialize log file with header
20
+ {
21
+ echo "=============================================="
22
+ echo "Pre-commit Setup Log"
23
+ echo "=============================================="
24
+ echo "Timestamp: $(date)"
25
+ echo "Repository: $repo_name"
26
+ echo "User: $(whoami)"
27
+ echo "Platform: $(uname -s)"
28
+ echo "=============================================="
29
+ echo ""
30
+ } > "$LOG_FILE"
31
+
32
+ export LOG_FILE
33
+ export LOG_DIR
34
+
35
+ echo "Logging to: $LOG_FILE" >&2
36
+ }
37
+
38
+ # Get timestamp for log entries
39
+ _log_timestamp() {
40
+ date +"%Y-%m-%d %H:%M:%S"
41
+ }
42
+
43
+ # Core logging function
44
+ _log() {
45
+ local level="$1"
46
+ local message="$2"
47
+ local timestamp
48
+ timestamp=$(_log_timestamp)
49
+
50
+ # Write to log file (without colors)
51
+ if [[ -n "${LOG_FILE:-}" ]]; then
52
+ echo "[$timestamp] [$level] $message" >> "$LOG_FILE"
53
+ fi
54
+ }
55
+
56
+ # Colors for console output
57
+ RED='\033[0;31m'
58
+ GREEN='\033[0;32m'
59
+ YELLOW='\033[1;33m'
60
+ BLUE='\033[0;34m'
61
+ # CYAN is available for future use
62
+ # shellcheck disable=SC2034
63
+ CYAN='\033[0;36m'
64
+ NC='\033[0m' # No Color
65
+
66
+ # Logging functions with console colors and file output
67
+ # All logs go to stderr to avoid polluting stdout (which is used for return values)
68
+ log_info() {
69
+ local message="$1"
70
+ echo -e "${GREEN}[INFO]${NC} $message" >&2
71
+ _log "INFO" "$message"
72
+ }
73
+
74
+ log_warn() {
75
+ local message="$1"
76
+ echo -e "${YELLOW}[WARN]${NC} $message" >&2
77
+ _log "WARN" "$message"
78
+ }
79
+
80
+ log_error() {
81
+ local message="$1"
82
+ echo -e "${RED}[ERROR]${NC} $message" >&2
83
+ _log "ERROR" "$message"
84
+ }
85
+
86
+ log_debug() {
87
+ local message="$1"
88
+ if [[ "${DEBUG:-false}" == "true" ]]; then
89
+ echo -e "${BLUE}[DEBUG]${NC} $message" >&2
90
+ fi
91
+ _log "DEBUG" "$message"
92
+ }
93
+
94
+ log_phase() {
95
+ local phase_num="$1"
96
+ local phase_name="$2"
97
+
98
+ local separator="━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
99
+
100
+ echo "" >&2
101
+ echo -e "${BLUE}$separator${NC}" >&2
102
+ echo -e "${BLUE} PHASE $phase_num: $phase_name${NC}" >&2
103
+ echo -e "${BLUE}$separator${NC}" >&2
104
+ echo "" >&2
105
+
106
+ _log "PHASE" "========== PHASE $phase_num: $phase_name =========="
107
+ }
108
+
109
+ # Log command execution with output capture
110
+ log_cmd() {
111
+ local _description="$1" # Used for documentation, kept for API compatibility
112
+ shift
113
+ local cmd="$*"
114
+
115
+ log_debug "Executing ($_description): $cmd"
116
+ _log "CMD" ">>> $cmd"
117
+
118
+ # Execute and capture output
119
+ local output
120
+ local exit_code
121
+ output=$("$@" 2>&1) || exit_code=$?
122
+ exit_code=${exit_code:-0}
123
+
124
+ # Log output to file
125
+ if [[ -n "${LOG_FILE:-}" ]] && [[ -n "$output" ]]; then
126
+ echo "$output" >> "$LOG_FILE"
127
+ fi
128
+
129
+ _log "CMD" "Exit code: $exit_code"
130
+
131
+ # Return output and exit code
132
+ echo "$output"
133
+ return "$exit_code"
134
+ }
135
+
136
+ # Log a section divider
137
+ log_section() {
138
+ local title="$1"
139
+ _log "SECTION" "--- $title ---"
140
+ echo -e "${GREEN}[INFO]${NC} --- $title ---" >&2
141
+ }
142
+
143
+ # Log raw content (e.g., config files, API responses)
144
+ log_content() {
145
+ local label="$1"
146
+ local content="$2"
147
+ local max_lines="${3:-50}"
148
+
149
+ _log "CONTENT" "=== $label (first $max_lines lines) ==="
150
+
151
+ if [[ -n "${LOG_FILE:-}" ]]; then
152
+ echo "$content" | head -n "$max_lines" >> "$LOG_FILE"
153
+ local total_lines
154
+ total_lines=$(echo "$content" | wc -l)
155
+ if [[ $total_lines -gt $max_lines ]]; then
156
+ echo "... ($total_lines total lines, truncated)" >> "$LOG_FILE"
157
+ fi
158
+ fi
159
+ }
160
+
161
+ # Get path to current log file
162
+ get_log_file() {
163
+ echo "${LOG_FILE:-}"
164
+ }
165
+
166
+ # List recent log files
167
+ list_recent_logs() {
168
+ local count="${1:-10}"
169
+ local log_dir="/tmp/precommit-setup"
170
+
171
+ if [[ -d "$log_dir" ]]; then
172
+ echo "Recent log files:" >&2
173
+ # shellcheck disable=SC2012
174
+ ls -lt "$log_dir"/*.log 2>/dev/null | head -n "$count" | while read -r line; do
175
+ echo " $line" >&2
176
+ done
177
+ else
178
+ echo "No logs found in $log_dir" >&2
179
+ fi
180
+ }
181
+
182
+ # Finalize logging - add summary
183
+ finalize_logging() {
184
+ local exit_code="${1:-0}"
185
+
186
+ if [[ -n "${LOG_FILE:-}" ]]; then
187
+ {
188
+ echo ""
189
+ echo "=============================================="
190
+ echo "Log completed at: $(date)"
191
+ echo "Exit code: $exit_code"
192
+ echo "=============================================="
193
+ } >> "$LOG_FILE"
194
+ fi
195
+
196
+ if [[ $exit_code -eq 0 ]]; then
197
+ log_info "Full log available at: $LOG_FILE"
198
+ else
199
+ log_error "Full log available at: $LOG_FILE"
200
+ fi
201
+ }