forked from mirrors/kingfisher
Added Husky precommit support and added pre-commit hook that automatically downloads and caches the appropriate binary for your platform (no Docker or manual installation required).
This commit is contained in:
parent
5eb743711b
commit
45cab25615
6 changed files with 548 additions and 4 deletions
148
scripts/install-husky.sh
Executable file
148
scripts/install-husky.sh
Executable file
|
|
@ -0,0 +1,148 @@
|
|||
#!/usr/bin/env bash
|
||||
# Install Kingfisher as a Husky pre-commit hook
|
||||
# Usage: ./install-husky.sh [--uninstall]
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'USAGE'
|
||||
Usage: install-husky.sh [OPTIONS]
|
||||
|
||||
Adds Kingfisher to your Husky pre-commit hook.
|
||||
|
||||
Options:
|
||||
--uninstall Remove Kingfisher from the Husky pre-commit hook
|
||||
--use-docker Use Docker instead of local binary (no installation needed)
|
||||
--auto-install Auto-download kingfisher binary if not present
|
||||
-h, --help Show this help message
|
||||
|
||||
Requirements:
|
||||
- Node.js project with Husky already initialized
|
||||
- OR run this script after 'npx husky init'
|
||||
|
||||
USAGE
|
||||
}
|
||||
|
||||
UNINSTALL=false
|
||||
USE_DOCKER=false
|
||||
AUTO_INSTALL=false
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--uninstall)
|
||||
UNINSTALL=true
|
||||
shift
|
||||
;;
|
||||
--use-docker)
|
||||
USE_DOCKER=true
|
||||
shift
|
||||
;;
|
||||
--auto-install)
|
||||
AUTO_INSTALL=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option: $1" >&2
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Find Husky directory
|
||||
find_husky_dir() {
|
||||
if [[ -d ".husky" ]]; then
|
||||
echo ".husky"
|
||||
elif [[ -d ".config/husky" ]]; then
|
||||
echo ".config/husky"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
HUSKY_DIR="$(find_husky_dir)"
|
||||
|
||||
if [[ -z "$HUSKY_DIR" ]]; then
|
||||
echo "Error: Husky directory not found." >&2
|
||||
echo "Initialize Husky first with: npx husky init" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PRE_COMMIT="$HUSKY_DIR/pre-commit"
|
||||
MARKER="# kingfisher-scan"
|
||||
|
||||
# Determine the scan command
|
||||
if $USE_DOCKER; then
|
||||
SCAN_CMD='docker run --rm -v "$(pwd)":/src ghcr.io/mongodb/kingfisher:latest scan /src --staged --quiet --no-update-check'
|
||||
elif $AUTO_INSTALL; then
|
||||
# Use the auto-download script approach
|
||||
SCAN_CMD='curl -fsSL https://raw.githubusercontent.com/mongodb/kingfisher/main/scripts/kingfisher-pre-commit-auto.sh | bash'
|
||||
else
|
||||
SCAN_CMD='kingfisher scan . --staged --quiet --no-update-check'
|
||||
fi
|
||||
|
||||
uninstall() {
|
||||
if [[ -f "$PRE_COMMIT" ]]; then
|
||||
# Remove kingfisher lines from pre-commit
|
||||
if grep -q "$MARKER" "$PRE_COMMIT"; then
|
||||
# Create temp file without kingfisher lines
|
||||
local tmpfile
|
||||
tmpfile="$(mktemp)"
|
||||
grep -v "$MARKER" "$PRE_COMMIT" | grep -v "kingfisher scan\|kingfisher:latest\|kingfisher-pre-commit-auto" > "$tmpfile" || true
|
||||
mv "$tmpfile" "$PRE_COMMIT"
|
||||
chmod +x "$PRE_COMMIT"
|
||||
echo "Kingfisher removed from $PRE_COMMIT"
|
||||
else
|
||||
echo "Kingfisher not found in $PRE_COMMIT"
|
||||
fi
|
||||
else
|
||||
echo "No pre-commit hook found at $PRE_COMMIT"
|
||||
fi
|
||||
}
|
||||
|
||||
install() {
|
||||
# Create pre-commit if it doesn't exist
|
||||
if [[ ! -f "$PRE_COMMIT" ]]; then
|
||||
cat > "$PRE_COMMIT" <<'EOF'
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
EOF
|
||||
chmod +x "$PRE_COMMIT"
|
||||
echo "Created $PRE_COMMIT"
|
||||
fi
|
||||
|
||||
# Check if kingfisher is already installed
|
||||
if grep -q "$MARKER" "$PRE_COMMIT" 2>/dev/null; then
|
||||
echo "Kingfisher is already configured in $PRE_COMMIT"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Append kingfisher scan command
|
||||
cat >> "$PRE_COMMIT" <<EOF
|
||||
|
||||
$MARKER
|
||||
$SCAN_CMD
|
||||
EOF
|
||||
|
||||
echo "Kingfisher added to $PRE_COMMIT"
|
||||
|
||||
if ! $USE_DOCKER && ! $AUTO_INSTALL; then
|
||||
if ! command -v kingfisher >/dev/null 2>&1; then
|
||||
echo ""
|
||||
echo "Note: kingfisher is not installed. Install it with:"
|
||||
echo " curl -fsSL https://raw.githubusercontent.com/mongodb/kingfisher/main/scripts/install-kingfisher.sh | bash"
|
||||
echo ""
|
||||
echo "Or re-run this script with --use-docker or --auto-install"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
if $UNINSTALL; then
|
||||
uninstall
|
||||
else
|
||||
install
|
||||
fi
|
||||
143
scripts/kingfisher-pre-commit-auto.ps1
Normal file
143
scripts/kingfisher-pre-commit-auto.ps1
Normal file
|
|
@ -0,0 +1,143 @@
|
|||
<#
|
||||
.SYNOPSIS
|
||||
Kingfisher pre-commit hook with automatic binary download for Windows.
|
||||
|
||||
.DESCRIPTION
|
||||
Downloads and caches the Kingfisher binary, then scans staged changes.
|
||||
No manual installation required.
|
||||
|
||||
.PARAMETER Version
|
||||
Specific version to download (e.g., "1.76.0" or "v1.76.0").
|
||||
Defaults to "latest".
|
||||
|
||||
.EXAMPLE
|
||||
./kingfisher-pre-commit-auto.ps1
|
||||
|
||||
.EXAMPLE
|
||||
$env:KINGFISHER_VERSION = "1.76.0"; ./kingfisher-pre-commit-auto.ps1
|
||||
#>
|
||||
[CmdletBinding()]
|
||||
param()
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
$repo = 'mongodb/kingfisher'
|
||||
$cacheDir = if ($env:KINGFISHER_CACHE_DIR) {
|
||||
$env:KINGFISHER_CACHE_DIR
|
||||
} else {
|
||||
Join-Path $env:LOCALAPPDATA 'kingfisher'
|
||||
}
|
||||
$kingfisherBin = Join-Path $cacheDir 'kingfisher.exe'
|
||||
$versionFile = Join-Path $cacheDir '.version'
|
||||
$expectedVersion = if ($env:KINGFISHER_VERSION) { $env:KINGFISHER_VERSION } else { 'latest' }
|
||||
|
||||
function Get-AssetName {
|
||||
# Windows only supports x64 currently
|
||||
return 'kingfisher-windows-x64.zip'
|
||||
}
|
||||
|
||||
function Download-Kingfisher {
|
||||
param(
|
||||
[string]$Version
|
||||
)
|
||||
|
||||
$assetName = Get-AssetName
|
||||
|
||||
if (-not (Test-Path $cacheDir)) {
|
||||
New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null
|
||||
}
|
||||
|
||||
$tempDir = New-Item -ItemType Directory -Path ([System.IO.Path]::GetTempPath()) -Name ([System.Guid]::NewGuid().ToString())
|
||||
|
||||
try {
|
||||
if ($Version -eq 'latest') {
|
||||
$downloadUrl = "https://github.com/$repo/releases/latest/download/$assetName"
|
||||
Write-Host "Downloading kingfisher (latest) for Windows..." -ForegroundColor Cyan
|
||||
} else {
|
||||
# Support both "v1.76.0" and "1.76.0" formats
|
||||
if (-not $Version.StartsWith('v')) {
|
||||
$Version = "v$Version"
|
||||
}
|
||||
$downloadUrl = "https://github.com/$repo/releases/download/$Version/$assetName"
|
||||
Write-Host "Downloading kingfisher ($Version) for Windows..." -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
$archivePath = Join-Path $tempDir.FullName $assetName
|
||||
|
||||
try {
|
||||
Invoke-WebRequest -Uri $downloadUrl -OutFile $archivePath -UseBasicParsing
|
||||
} catch {
|
||||
Write-Error "Failed to download $downloadUrl : $_"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "Extracting archive..." -ForegroundColor Cyan
|
||||
Expand-Archive -Path $archivePath -DestinationPath $tempDir.FullName -Force
|
||||
|
||||
$extractedBinary = Join-Path $tempDir.FullName 'kingfisher.exe'
|
||||
if (-not (Test-Path $extractedBinary)) {
|
||||
Write-Error "Binary not found in downloaded archive"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Copy-Item -Path $extractedBinary -Destination $kingfisherBin -Force
|
||||
|
||||
# Store the version we downloaded
|
||||
if ($Version -eq 'latest') {
|
||||
try {
|
||||
$versionOutput = & $kingfisherBin --version 2>$null | Select-Object -First 1
|
||||
Set-Content -Path $versionFile -Value $versionOutput -NoNewline
|
||||
} catch {
|
||||
Set-Content -Path $versionFile -Value 'latest' -NoNewline
|
||||
}
|
||||
} else {
|
||||
Set-Content -Path $versionFile -Value $Version -NoNewline
|
||||
}
|
||||
|
||||
Write-Host "Kingfisher installed to $kingfisherBin" -ForegroundColor Green
|
||||
}
|
||||
finally {
|
||||
if ($tempDir -and (Test-Path $tempDir.FullName)) {
|
||||
Remove-Item -Path $tempDir.FullName -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Test-NeedsDownload {
|
||||
# Binary doesn't exist
|
||||
if (-not (Test-Path $kingfisherBin)) {
|
||||
return $true
|
||||
}
|
||||
|
||||
# No version tracking - always use existing binary for 'latest'
|
||||
if ($expectedVersion -eq 'latest') {
|
||||
return $false
|
||||
}
|
||||
|
||||
# Check if version matches
|
||||
if (Test-Path $versionFile) {
|
||||
$installedVersion = Get-Content -Path $versionFile -Raw
|
||||
|
||||
# Normalize version format for comparison
|
||||
$expectedNormalized = $expectedVersion
|
||||
if (-not $expectedNormalized.StartsWith('v')) {
|
||||
$expectedNormalized = "v$expectedNormalized"
|
||||
}
|
||||
|
||||
if ($installedVersion -like "*$expectedNormalized*" -or $installedVersion -eq $expectedVersion) {
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
return $true
|
||||
}
|
||||
|
||||
# Main execution
|
||||
if (Test-NeedsDownload) {
|
||||
Download-Kingfisher -Version $expectedVersion
|
||||
}
|
||||
|
||||
# Run kingfisher scan on staged changes
|
||||
# Pass through any additional arguments
|
||||
& $kingfisherBin scan . --staged --quiet --no-update-check @args
|
||||
exit $LASTEXITCODE
|
||||
136
scripts/kingfisher-pre-commit-auto.sh
Executable file
136
scripts/kingfisher-pre-commit-auto.sh
Executable file
|
|
@ -0,0 +1,136 @@
|
|||
#!/usr/bin/env bash
|
||||
# Kingfisher pre-commit hook with automatic binary download
|
||||
# This script downloads the appropriate kingfisher binary if not already cached,
|
||||
# then runs the scan against staged changes.
|
||||
set -euo pipefail
|
||||
|
||||
REPO="mongodb/kingfisher"
|
||||
CACHE_DIR="${KINGFISHER_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/kingfisher}"
|
||||
KINGFISHER_BIN="$CACHE_DIR/kingfisher"
|
||||
VERSION_FILE="$CACHE_DIR/.version"
|
||||
|
||||
# Determine the expected version from the pre-commit rev (passed as env var or default to latest)
|
||||
EXPECTED_VERSION="${KINGFISHER_VERSION:-latest}"
|
||||
|
||||
get_platform() {
|
||||
local os arch
|
||||
os="$(uname -s)"
|
||||
arch="$(uname -m)"
|
||||
|
||||
case "$os" in
|
||||
Linux) platform="linux" ;;
|
||||
Darwin) platform="darwin" ;;
|
||||
MINGW*|MSYS*|CYGWIN*) platform="windows" ;;
|
||||
*) echo "Error: Unsupported OS '$os'" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
case "$arch" in
|
||||
x86_64|amd64) arch_suffix="x64" ;;
|
||||
arm64|aarch64) arch_suffix="arm64" ;;
|
||||
*) echo "Error: Unsupported architecture '$arch'" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
echo "${platform}-${arch_suffix}"
|
||||
}
|
||||
|
||||
download_kingfisher() {
|
||||
local platform="$1"
|
||||
local version="$2"
|
||||
local ext="tgz"
|
||||
|
||||
if [[ "$platform" == windows-* ]]; then
|
||||
ext="zip"
|
||||
fi
|
||||
|
||||
local asset_name="kingfisher-${platform}.${ext}"
|
||||
local download_url
|
||||
|
||||
if [[ "$version" == "latest" ]]; then
|
||||
download_url="https://github.com/${REPO}/releases/latest/download/${asset_name}"
|
||||
else
|
||||
# Support both "v1.76.0" and "1.76.0" formats
|
||||
if [[ "$version" != v* ]]; then
|
||||
version="v${version}"
|
||||
fi
|
||||
download_url="https://github.com/${REPO}/releases/download/${version}/${asset_name}"
|
||||
fi
|
||||
|
||||
mkdir -p "$CACHE_DIR"
|
||||
local tmpdir
|
||||
tmpdir="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmpdir"' EXIT
|
||||
|
||||
echo "Downloading kingfisher ($version) for $platform..." >&2
|
||||
|
||||
if ! curl -fLsS "$download_url" -o "$tmpdir/$asset_name"; then
|
||||
echo "Error: Failed to download $download_url" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$ext" == "zip" ]]; then
|
||||
unzip -q "$tmpdir/$asset_name" -d "$tmpdir"
|
||||
local binary_name="kingfisher.exe"
|
||||
else
|
||||
tar -C "$tmpdir" -xzf "$tmpdir/$asset_name"
|
||||
local binary_name="kingfisher"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$tmpdir/$binary_name" ]]; then
|
||||
echo "Error: Binary not found in downloaded archive" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mv "$tmpdir/$binary_name" "$KINGFISHER_BIN"
|
||||
chmod +x "$KINGFISHER_BIN"
|
||||
|
||||
# Store the version we downloaded
|
||||
if [[ "$version" == "latest" ]]; then
|
||||
"$KINGFISHER_BIN" --version 2>/dev/null | head -1 > "$VERSION_FILE" || echo "latest" > "$VERSION_FILE"
|
||||
else
|
||||
echo "$version" > "$VERSION_FILE"
|
||||
fi
|
||||
|
||||
echo "Kingfisher installed to $KINGFISHER_BIN" >&2
|
||||
}
|
||||
|
||||
needs_download() {
|
||||
# Binary doesn't exist
|
||||
if [[ ! -x "$KINGFISHER_BIN" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
# No version tracking - always use existing binary for 'latest'
|
||||
if [[ "$EXPECTED_VERSION" == "latest" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Check if version matches
|
||||
if [[ -f "$VERSION_FILE" ]]; then
|
||||
local installed_version
|
||||
installed_version="$(cat "$VERSION_FILE")"
|
||||
# Normalize version format for comparison
|
||||
local expected_normalized="$EXPECTED_VERSION"
|
||||
if [[ "$expected_normalized" != v* ]]; then
|
||||
expected_normalized="v${expected_normalized}"
|
||||
fi
|
||||
if [[ "$installed_version" == *"$expected_normalized"* ]] || [[ "$installed_version" == "$EXPECTED_VERSION" ]]; then
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
main() {
|
||||
local platform
|
||||
platform="$(get_platform)"
|
||||
|
||||
if needs_download; then
|
||||
download_kingfisher "$platform" "$EXPECTED_VERSION"
|
||||
fi
|
||||
|
||||
# Run kingfisher scan on staged changes
|
||||
exec "$KINGFISHER_BIN" scan . --staged --quiet --no-update-check "$@"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
Loading…
Add table
Add a link
Reference in a new issue