A new start
This commit is contained in:
34
.editorconfig
Executable file
34
.editorconfig
Executable file
@@ -0,0 +1,34 @@
|
||||
# EditorConfig is awesome: https://editorconfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Unix-style newlines with a newline ending every file
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_size = 2
|
||||
|
||||
# Matches multiple files with brace expansion notation
|
||||
# Set default charset
|
||||
[*.{js,py}]
|
||||
charset = utf-8
|
||||
|
||||
# 4 space indentation
|
||||
[*.py]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
||||
# Tab indentation (no size specified)
|
||||
[Makefile]
|
||||
indent_style = tab
|
||||
|
||||
# Indentation override for all JS under lib directory
|
||||
[lib/**.js]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Matches the exact files either package.json or .travis.yml
|
||||
[{package.json,.travis.yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
16
.gitconfig
Executable file
16
.gitconfig
Executable file
@@ -0,0 +1,16 @@
|
||||
[user]
|
||||
name = Sebastian Krüger
|
||||
email = valknar@pivoine.art
|
||||
[filter "lfs"]
|
||||
clean = git-lfs clean -- %f
|
||||
smudge = git-lfs smudge -- %f
|
||||
process = git-lfs filter-process
|
||||
required = true
|
||||
[core]
|
||||
editor = nano
|
||||
[init]
|
||||
defaultBranch = main
|
||||
[alias]
|
||||
squash-all = "!f(){ git reset $(git commit-tree \"HEAD^{tree}\" \"$@\");};f"
|
||||
[push]
|
||||
autoSetupRemote = true
|
||||
55
.gitignore
vendored
Executable file
55
.gitignore
vendored
Executable file
@@ -0,0 +1,55 @@
|
||||
# Ignore everything
|
||||
*
|
||||
|
||||
# But not these files.
|
||||
!CLAUDE.md
|
||||
!.gitignore
|
||||
!.gitconfig
|
||||
!.gitmodules
|
||||
!README.md
|
||||
!.editorconfig
|
||||
!*.env
|
||||
!.prettierrc
|
||||
!.prettierignore
|
||||
!package.json
|
||||
!pnpm-workspace.yaml
|
||||
!.nvmrc
|
||||
!.ruby-version
|
||||
!.python-version
|
||||
!.pre-commit-config.yaml
|
||||
!.rubocop.yml
|
||||
!requirements.txt
|
||||
!Gemfile
|
||||
!ecosystem.config.js
|
||||
!playbook.yml
|
||||
!.p10k.zsh
|
||||
!crates.yml
|
||||
!flatpaks.yml
|
||||
!eslint.config.mts
|
||||
!.zprofile
|
||||
!.zlogout
|
||||
!.zlogin
|
||||
!.zshrc
|
||||
!.zshenv
|
||||
!.hushlogin
|
||||
!.last_pwd
|
||||
!biome.json
|
||||
!arty.yml
|
||||
|
||||
!/.github/
|
||||
!/.github/**
|
||||
|
||||
!/.init/
|
||||
!/.init/**
|
||||
|
||||
!.vscode/
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
|
||||
# Ignore so we won't commit these in the allowed dirctories.
|
||||
.DS_Store
|
||||
*.log*
|
||||
*.db*
|
||||
*.vscdb*
|
||||
*.sqlite*
|
||||
*.bnk*
|
||||
0
.hushlogin
Normal file
0
.hushlogin
Normal file
17
.init/alias.sh
Executable file
17
.init/alias.sh
Executable file
@@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
alias ri='source ~/.init/init.sh'
|
||||
|
||||
# git
|
||||
alias g0='git add . && git diff --quiet && git diff --cached --quiet'
|
||||
alias g1='git reset $(git commit-tree "HEAD^{tree}" -m "A new start")'
|
||||
alias g2='git log --format=%B -n 1 HEAD | head -n 1'
|
||||
|
||||
# rsync
|
||||
alias rs='rsync --rsync-path="sudo rsync" -avzhe ssh'
|
||||
|
||||
# serve static files
|
||||
alias ss='python -m http.server 8000 -d'
|
||||
|
||||
# download youtube mp3
|
||||
alias yt='yt-dlp -x --audio-format mp3'
|
||||
0
.init/bin/.gitkeep
Normal file
0
.init/bin/.gitkeep
Normal file
477
.init/bin/artifact_github_download.sh
Executable file
477
.init/bin/artifact_github_download.sh
Executable file
@@ -0,0 +1,477 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
#############################################################################
|
||||
# GitHub Artifact Downloader
|
||||
#
|
||||
# Download and extract GitHub Actions artifacts with style
|
||||
#
|
||||
# Usage:
|
||||
# artifact_github_download.sh <REPO> [OPTIONS]
|
||||
#
|
||||
# Arguments:
|
||||
# REPO GitHub repository (owner/repo)
|
||||
#
|
||||
# Options:
|
||||
# -n, --name NAME Artifact name to download (preselect)
|
||||
# -o, --output DIR Output directory (default: current directory)
|
||||
# -h, --help Show this help message
|
||||
#
|
||||
# Examples:
|
||||
# artifact_github_download.sh valknarness/awesome
|
||||
# artifact_github_download.sh valknarness/awesome -n awesome-database-latest
|
||||
# artifact_github_download.sh valknarness/awesome -o ~/downloads
|
||||
#############################################################################
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ============================================================================
|
||||
# Color Definitions
|
||||
# ============================================================================
|
||||
|
||||
# Check if terminal supports colors
|
||||
if [[ -t 1 ]] && command -v tput >/dev/null 2>&1; then
|
||||
COLORS=$(tput colors 2>/dev/null || echo 0)
|
||||
if [[ $COLORS -ge 8 ]]; then
|
||||
# Standard colors
|
||||
RED=$(tput setaf 1)
|
||||
GREEN=$(tput setaf 2)
|
||||
YELLOW=$(tput setaf 3)
|
||||
BLUE=$(tput setaf 4)
|
||||
MAGENTA=$(tput setaf 5)
|
||||
CYAN=$(tput setaf 6)
|
||||
WHITE=$(tput setaf 7)
|
||||
|
||||
# Bright colors
|
||||
BRIGHT_GREEN=$(tput setaf 10 2>/dev/null || tput setaf 2)
|
||||
BRIGHT_YELLOW=$(tput setaf 11 2>/dev/null || tput setaf 3)
|
||||
BRIGHT_BLUE=$(tput setaf 12 2>/dev/null || tput setaf 4)
|
||||
BRIGHT_MAGENTA=$(tput setaf 13 2>/dev/null || tput setaf 5)
|
||||
BRIGHT_CYAN=$(tput setaf 14 2>/dev/null || tput setaf 6)
|
||||
|
||||
# Text formatting
|
||||
BOLD=$(tput bold)
|
||||
DIM=$(tput dim 2>/dev/null || echo "")
|
||||
RESET=$(tput sgr0)
|
||||
else
|
||||
RED="" GREEN="" YELLOW="" BLUE="" MAGENTA="" CYAN="" WHITE=""
|
||||
BRIGHT_GREEN="" BRIGHT_YELLOW="" BRIGHT_BLUE="" BRIGHT_MAGENTA="" BRIGHT_CYAN=""
|
||||
BOLD="" DIM="" RESET=""
|
||||
fi
|
||||
else
|
||||
RED="" GREEN="" YELLOW="" BLUE="" MAGENTA="" CYAN="" WHITE=""
|
||||
BRIGHT_GREEN="" BRIGHT_YELLOW="" BRIGHT_BLUE="" BRIGHT_MAGENTA="" BRIGHT_CYAN=""
|
||||
BOLD="" DIM="" RESET=""
|
||||
fi
|
||||
|
||||
# ============================================================================
|
||||
# Logging Functions
|
||||
# ============================================================================
|
||||
|
||||
log_info() {
|
||||
echo -e "${BRIGHT_BLUE}${BOLD}ℹ${RESET} ${CYAN}$*${RESET}" >&2
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${BRIGHT_GREEN}${BOLD}✓${RESET} ${GREEN}$*${RESET}" >&2
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${BRIGHT_YELLOW}${BOLD}⚠${RESET} ${YELLOW}$*${RESET}" >&2
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}${BOLD}✗${RESET} ${RED}$*${RESET}" >&2
|
||||
}
|
||||
|
||||
log_step() {
|
||||
echo -e "${BRIGHT_MAGENTA}${BOLD}▸${RESET} ${MAGENTA}$*${RESET}" >&2
|
||||
}
|
||||
|
||||
log_header() {
|
||||
local text="$*"
|
||||
local length=${#text}
|
||||
local line=$(printf '═%.0s' $(seq 1 $length))
|
||||
echo "" >&2
|
||||
echo -e "${BRIGHT_CYAN}${BOLD}╔${line}╗${RESET}" >&2
|
||||
echo -e "${BRIGHT_CYAN}${BOLD}║${RESET}${BOLD}${WHITE}${text}${RESET}${BRIGHT_CYAN}${BOLD}║${RESET}" >&2
|
||||
echo -e "${BRIGHT_CYAN}${BOLD}╚${line}╝${RESET}" >&2
|
||||
echo "" >&2
|
||||
}
|
||||
|
||||
log_data() {
|
||||
local label="$1"
|
||||
local value="$2"
|
||||
echo -e " ${DIM}${label}:${RESET} ${BOLD}${value}${RESET}" >&2
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Helper Functions
|
||||
# ============================================================================
|
||||
|
||||
check_dependencies() {
|
||||
local missing=()
|
||||
|
||||
if ! command -v gh &> /dev/null; then
|
||||
missing+=("gh (GitHub CLI)")
|
||||
fi
|
||||
|
||||
if ! command -v jq &> /dev/null; then
|
||||
missing+=("jq")
|
||||
fi
|
||||
|
||||
if ! command -v unzip &> /dev/null; then
|
||||
missing+=("unzip")
|
||||
fi
|
||||
|
||||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||
log_error "Missing required dependencies:"
|
||||
for dep in "${missing[@]}"; do
|
||||
echo -e " ${RED}•${RESET} ${dep}"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
check_gh_auth() {
|
||||
if ! gh auth status &> /dev/null; then
|
||||
log_error "Not authenticated with GitHub CLI"
|
||||
log_info "Run: ${BOLD}gh auth login${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
${BOLD}${BRIGHT_CYAN}GitHub Artifact Downloader${RESET}
|
||||
|
||||
${BOLD}USAGE:${RESET}
|
||||
$(basename "$0") ${CYAN}<REPO>${RESET} [${YELLOW}OPTIONS${RESET}]
|
||||
|
||||
${BOLD}ARGUMENTS:${RESET}
|
||||
${CYAN}REPO${RESET} GitHub repository (${DIM}owner/repo${RESET})
|
||||
|
||||
${BOLD}OPTIONS:${RESET}
|
||||
${YELLOW}-n, --name NAME${RESET} Artifact name to download (preselect)
|
||||
${YELLOW}-o, --output DIR${RESET} Output directory (default: current directory)
|
||||
${YELLOW}-h, --help${RESET} Show this help message
|
||||
|
||||
${BOLD}EXAMPLES:${RESET}
|
||||
${DIM}# Interactive mode - list and select artifacts${RESET}
|
||||
$(basename "$0") valknarness/awesome
|
||||
|
||||
${DIM}# Preselect artifact by name${RESET}
|
||||
$(basename "$0") valknarness/awesome -n awesome-database-latest
|
||||
|
||||
${DIM}# Download to specific directory${RESET}
|
||||
$(basename "$0") valknarness/awesome -o ~/downloads
|
||||
|
||||
${DIM}# Combine options${RESET}
|
||||
$(basename "$0") valknarness/awesome -n awesome-database-latest -o ~/downloads
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
format_size() {
|
||||
local bytes=$1
|
||||
if (( bytes < 1024 )); then
|
||||
echo "${bytes}B"
|
||||
elif (( bytes < 1048576 )); then
|
||||
awk "BEGIN {printf \"%.1fKB\", $bytes/1024}"
|
||||
elif (( bytes < 1073741824 )); then
|
||||
awk "BEGIN {printf \"%.1fMB\", $bytes/1048576}"
|
||||
else
|
||||
awk "BEGIN {printf \"%.2fGB\", $bytes/1073741824}"
|
||||
fi
|
||||
}
|
||||
|
||||
format_date() {
|
||||
local iso_date="$1"
|
||||
if command -v date &> /dev/null; then
|
||||
if date --version &> /dev/null 2>&1; then
|
||||
# GNU date
|
||||
date -d "$iso_date" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "$iso_date"
|
||||
else
|
||||
# BSD date (macOS)
|
||||
date -j -f "%Y-%m-%dT%H:%M:%SZ" "$iso_date" "+%Y-%m-%d %H:%M:%S" 2>/dev/null || echo "$iso_date"
|
||||
fi
|
||||
else
|
||||
echo "$iso_date"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main Functions
|
||||
# ============================================================================
|
||||
|
||||
list_artifacts() {
|
||||
local repo="$1"
|
||||
|
||||
log_step "Fetching artifacts from ${BOLD}${repo}${RESET}..."
|
||||
|
||||
# First check if there are any artifacts using gh's built-in jq
|
||||
local count
|
||||
count=$(gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
--jq '.artifacts | length' \
|
||||
"/repos/${repo}/actions/artifacts?per_page=100" 2>/dev/null)
|
||||
|
||||
if [[ -z "$count" ]]; then
|
||||
log_error "Failed to fetch artifacts from repository"
|
||||
log_info "Please check that:"
|
||||
echo " • The repository ${BOLD}${repo}${RESET} exists and you have access"
|
||||
echo " • GitHub Actions is enabled for this repository"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$count" -eq 0 ]]; then
|
||||
log_warning "No artifacts found in repository ${BOLD}${repo}${RESET}"
|
||||
log_info "This repository may not have any workflow runs that produced artifacts"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Now fetch the full JSON response
|
||||
local artifacts_json
|
||||
artifacts_json=$(gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/${repo}/actions/artifacts?per_page=100" 2>/dev/null)
|
||||
|
||||
echo "$artifacts_json"
|
||||
}
|
||||
|
||||
select_artifact() {
|
||||
local artifacts_json="$1"
|
||||
local preselect_name="$2"
|
||||
|
||||
# Parse artifacts
|
||||
local artifacts
|
||||
artifacts=$(echo "$artifacts_json" | jq -r '.artifacts[] |
|
||||
"\(.id)|\(.name)|\(.size_in_bytes)|\(.created_at)|\(.workflow_run.id)"')
|
||||
|
||||
# If preselect name is provided, find matching artifact
|
||||
if [[ -n "$preselect_name" ]]; then
|
||||
local selected
|
||||
selected=$(echo "$artifacts" | grep -F "|${preselect_name}|" | head -1)
|
||||
|
||||
if [[ -z "$selected" ]]; then
|
||||
log_error "Artifact '${BOLD}${preselect_name}${RESET}' not found"
|
||||
log_info "Available artifacts:"
|
||||
echo "$artifacts" | while IFS='|' read -r id name size created workflow; do
|
||||
echo " ${CYAN}•${RESET} ${name}"
|
||||
done
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "$selected"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Interactive selection
|
||||
log_info "Available artifacts:"
|
||||
echo ""
|
||||
|
||||
local i=1
|
||||
local -a artifact_array
|
||||
|
||||
while IFS='|' read -r id name size created workflow; do
|
||||
artifact_array+=("$id|$name|$size|$created|$workflow")
|
||||
local formatted_size=$(format_size "$size")
|
||||
local formatted_date=$(format_date "$created")
|
||||
|
||||
printf " ${BOLD}${YELLOW}[%2d]${RESET} ${BRIGHT_CYAN}%s${RESET}\n" "$i" "$name"
|
||||
printf " ${DIM}Size: ${RESET}%s ${DIM}Created: ${RESET}%s\n" "$formatted_size" "$formatted_date"
|
||||
echo ""
|
||||
|
||||
((i++))
|
||||
done <<< "$artifacts"
|
||||
|
||||
# Prompt for selection
|
||||
local selection
|
||||
while true; do
|
||||
echo -n -e "${BRIGHT_MAGENTA}${BOLD}<EFBFBD>${RESET} ${MAGENTA}Select artifact [1-$((i-1))]:${RESET} "
|
||||
read -r selection
|
||||
|
||||
if [[ "$selection" =~ ^[0-9]+$ ]] && [[ "$selection" -ge 1 ]] && [[ "$selection" -lt "$i" ]]; then
|
||||
break
|
||||
else
|
||||
log_warning "Invalid selection. Please enter a number between 1 and $((i-1))"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "${artifact_array[$((selection-1))]}"
|
||||
}
|
||||
|
||||
download_artifact() {
|
||||
local repo="$1"
|
||||
local artifact_id="$2"
|
||||
local artifact_name="$3"
|
||||
local output_dir="$4"
|
||||
|
||||
log_step "Downloading artifact ${BOLD}${artifact_name}${RESET}..."
|
||||
|
||||
# Create output directory if it doesn't exist
|
||||
mkdir -p "$output_dir"
|
||||
|
||||
# Download artifact using gh
|
||||
local zip_file="${output_dir}/${artifact_name}.zip"
|
||||
|
||||
if gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/${repo}/actions/artifacts/${artifact_id}/zip" \
|
||||
> "$zip_file" 2>/dev/null; then
|
||||
|
||||
log_success "Downloaded to ${BOLD}${zip_file}${RESET}"
|
||||
echo "$zip_file"
|
||||
else
|
||||
log_error "Failed to download artifact"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
extract_artifact() {
|
||||
local zip_file="$1"
|
||||
local output_dir="$2"
|
||||
|
||||
log_step "Extracting archive..."
|
||||
|
||||
# Create extraction directory
|
||||
local extract_dir="${output_dir}/$(basename "$zip_file" .zip)"
|
||||
mkdir -p "$extract_dir"
|
||||
|
||||
if unzip -q "$zip_file" -d "$extract_dir"; then
|
||||
log_success "Extracted to ${BOLD}${extract_dir}${RESET}"
|
||||
|
||||
# Show extracted files
|
||||
log_info "Extracted files:"
|
||||
find "$extract_dir" -type f -exec basename {} \; | while read -r file; do
|
||||
echo " ${GREEN}•${RESET} ${file}"
|
||||
done
|
||||
|
||||
# Remove zip file
|
||||
rm "$zip_file"
|
||||
log_info "Cleaned up zip file"
|
||||
|
||||
echo "$extract_dir"
|
||||
else
|
||||
log_error "Failed to extract archive"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Main Script
|
||||
# ============================================================================
|
||||
|
||||
main() {
|
||||
local repo=""
|
||||
local artifact_name=""
|
||||
local output_dir="."
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
-h|--help)
|
||||
show_help
|
||||
exit 0
|
||||
;;
|
||||
-n|--name)
|
||||
artifact_name="$2"
|
||||
shift 2
|
||||
;;
|
||||
-o|--output)
|
||||
output_dir="$2"
|
||||
shift 2
|
||||
;;
|
||||
-*)
|
||||
log_error "Unknown option: $1"
|
||||
echo ""
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$repo" ]]; then
|
||||
repo="$1"
|
||||
else
|
||||
log_error "Unexpected argument: $1"
|
||||
echo ""
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate required arguments
|
||||
if [[ -z "$repo" ]]; then
|
||||
log_error "Repository argument is required"
|
||||
echo ""
|
||||
show_help
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Validate repository format
|
||||
if [[ ! "$repo" =~ ^[^/]+/[^/]+$ ]]; then
|
||||
log_error "Invalid repository format. Expected: ${BOLD}owner/repo${RESET}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Show header
|
||||
log_header "GitHub Artifact Downloader"
|
||||
|
||||
# Check dependencies
|
||||
log_step "Checking dependencies..."
|
||||
check_dependencies
|
||||
log_success "All dependencies found"
|
||||
|
||||
# Check GitHub authentication
|
||||
log_step "Checking GitHub authentication..."
|
||||
check_gh_auth
|
||||
log_success "Authenticated with GitHub"
|
||||
|
||||
echo ""
|
||||
log_data "Repository" "${BRIGHT_CYAN}${repo}${RESET}"
|
||||
if [[ -n "$artifact_name" ]]; then
|
||||
log_data "Artifact" "${BRIGHT_YELLOW}${artifact_name}${RESET}"
|
||||
fi
|
||||
log_data "Output" "${BRIGHT_GREEN}${output_dir}${RESET}"
|
||||
echo ""
|
||||
|
||||
# List artifacts
|
||||
local artifacts_json
|
||||
artifacts_json=$(list_artifacts "$repo")
|
||||
|
||||
# Select artifact
|
||||
local selected
|
||||
selected=$(select_artifact "$artifacts_json" "$artifact_name")
|
||||
|
||||
IFS='|' read -r artifact_id name size created workflow <<< "$selected"
|
||||
|
||||
echo ""
|
||||
log_info "Selected artifact:"
|
||||
log_data " Name" "${BRIGHT_CYAN}${name}${RESET}"
|
||||
log_data " Size" "$(format_size "$size")"
|
||||
log_data " Created" "$(format_date "$created")"
|
||||
echo ""
|
||||
|
||||
# Download artifact
|
||||
local zip_file
|
||||
zip_file=$(download_artifact "$repo" "$artifact_id" "$name" "$output_dir")
|
||||
|
||||
# Extract artifact
|
||||
local extract_dir
|
||||
extract_dir=$(extract_artifact "$zip_file" "$output_dir")
|
||||
|
||||
# Success summary
|
||||
echo ""
|
||||
log_header "Download Complete!"
|
||||
log_data "Location" "${BOLD}${extract_dir}${RESET}"
|
||||
echo ""
|
||||
|
||||
log_success "All done! 🎉"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
592
.init/bin/mime_mp4_gif.sh
Executable file
592
.init/bin/mime_mp4_gif.sh
Executable file
@@ -0,0 +1,592 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# mime_mp4_gif.sh - Advanced MP4 to Animated GIF converter
|
||||
# Converts MP4 videos to GIFs with sophisticated keyframe extraction,
|
||||
# interpolation algorithms, and scheduling distributions
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Default values
|
||||
KEYFRAMES=10
|
||||
INPUT_SCHEDULES=1
|
||||
TRANSITION="linear"
|
||||
SCHEDULE="uniform"
|
||||
MAGIC="none"
|
||||
KEYFRAME_DURATION=100
|
||||
INPUT_FILE=""
|
||||
OUTPUT_FILE=""
|
||||
VERBOSE=false
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Available algorithms
|
||||
TRANSITIONS=("linear" "sinoid" "cubic" "quadratic" "exponential" "bounce" "elastic")
|
||||
SCHEDULES=("uniform" "front-load" "back-load" "center-peak" "edge-peak" "fibonacci" "golden-ratio")
|
||||
MAGICS=("none" "psychedelic" "dither-bloom" "edge-glow" "temporal-blur" "chromatic-shift" "vaporwave")
|
||||
|
||||
#############################################################################
|
||||
# Helper Functions
|
||||
#############################################################################
|
||||
|
||||
print_usage() {
|
||||
cat << EOF
|
||||
Usage: $(basename "$0") [OPTIONS] INPUT_FILE [OUTPUT_FILE]
|
||||
|
||||
Convert MP4 videos to animated GIFs with advanced frame extraction algorithms.
|
||||
|
||||
Arguments:
|
||||
INPUT_FILE Input MP4 video file (required)
|
||||
OUTPUT_FILE Output GIF file (optional, defaults to INPUT_FILE.gif)
|
||||
|
||||
Options:
|
||||
-k, --keyframes N Number of keyframes to extract (default: 10)
|
||||
-d, --keyframe-duration MS Duration of each frame in milliseconds (default: 100)
|
||||
Valid range: 1-30000 ms
|
||||
Lower values = faster animation
|
||||
Higher values = slower animation
|
||||
-i, --input-schedules N Number of input schedules (default: 1)
|
||||
1 schedule = entire video duration
|
||||
N schedules = divide video into N segments
|
||||
-t, --transition TYPE Interpolation function for frame timing
|
||||
Available: ${TRANSITIONS[*]}
|
||||
(default: linear)
|
||||
-s, --schedule TYPE Algorithm to distribute keyframes across schedules
|
||||
Available: ${SCHEDULES[*]}
|
||||
(default: uniform)
|
||||
-m, --magic TYPE Apply magical effects to the GIF
|
||||
Available: ${MAGICS[*]}
|
||||
(default: none)
|
||||
-v, --verbose Enable verbose output
|
||||
-h, --help Show this help message
|
||||
|
||||
Examples:
|
||||
# Basic conversion with 15 keyframes
|
||||
$(basename "$0") -k 15 video.mp4
|
||||
|
||||
# Fast animation with 50ms per frame
|
||||
$(basename "$0") -k 20 -d 50 video.mp4
|
||||
|
||||
# Slow animation with 500ms per frame
|
||||
$(basename "$0") -k 10 -d 500 video.mp4
|
||||
|
||||
# Use sinusoidal transition with center-peak distribution
|
||||
$(basename "$0") -t sinoid -s center-peak -k 20 video.mp4
|
||||
|
||||
# Apply psychedelic magic with fibonacci distribution
|
||||
$(basename "$0") -m psychedelic -s fibonacci -k 13 video.mp4 trippy.gif
|
||||
|
||||
# Complex: 3 schedules with cubic interpolation and edge glow
|
||||
$(basename "$0") -i 3 -t cubic -s front-load -m edge-glow -k 30 video.mp4
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $*"
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $*"
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $*"
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $*" >&2
|
||||
}
|
||||
|
||||
verbose_log() {
|
||||
if [[ "$VERBOSE" == "true" ]]; then
|
||||
log_info "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
validate_enum() {
|
||||
local value="$1"
|
||||
local array_name="$2"
|
||||
local -n arr=$array_name
|
||||
|
||||
for item in "${arr[@]}"; do
|
||||
if [[ "$value" == "$item" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Mathematical Functions
|
||||
#############################################################################
|
||||
|
||||
# Calculate transition weight based on interpolation type
|
||||
# Input: progress (0.0 to 1.0), Returns: weighted value (0.0 to 1.0)
|
||||
calculate_transition() {
|
||||
local progress="$1"
|
||||
local type="$2"
|
||||
|
||||
case "$type" in
|
||||
linear)
|
||||
echo "$progress"
|
||||
;;
|
||||
sinoid)
|
||||
# Smooth sinusoidal easing
|
||||
awk -v p="$progress" 'BEGIN { print (1 - cos(p * 3.14159265359)) / 2 }'
|
||||
;;
|
||||
cubic)
|
||||
# Cubic easing in-out
|
||||
awk -v p="$progress" 'BEGIN {
|
||||
if (p < 0.5)
|
||||
print 4 * p * p * p;
|
||||
else
|
||||
print 1 - ((-2 * p + 2) ^ 3) / 2;
|
||||
}'
|
||||
;;
|
||||
quadratic)
|
||||
# Quadratic easing
|
||||
awk -v p="$progress" 'BEGIN {
|
||||
if (p < 0.5)
|
||||
print 2 * p * p;
|
||||
else
|
||||
print 1 - ((-2 * p + 2) ^ 2) / 2;
|
||||
}'
|
||||
;;
|
||||
exponential)
|
||||
# Exponential easing
|
||||
awk -v p="$progress" 'BEGIN {
|
||||
if (p == 0) print 0;
|
||||
else if (p == 1) print 1;
|
||||
else if (p < 0.5) print (2 ^ (20 * p - 10)) / 2;
|
||||
else print (2 - (2 ^ (-20 * p + 10))) / 2;
|
||||
}'
|
||||
;;
|
||||
bounce)
|
||||
# Bouncing effect
|
||||
awk -v p="$progress" 'BEGIN {
|
||||
n1 = 7.5625; d1 = 2.75;
|
||||
x = 1 - p;
|
||||
if (x < 1/d1) result = n1 * x * x;
|
||||
else if (x < 2/d1) { x -= 1.5/d1; result = n1 * x * x + 0.75; }
|
||||
else if (x < 2.5/d1) { x -= 2.25/d1; result = n1 * x * x + 0.9375; }
|
||||
else { x -= 2.625/d1; result = n1 * x * x + 0.984375; }
|
||||
print 1 - result;
|
||||
}'
|
||||
;;
|
||||
elastic)
|
||||
# Elastic spring effect
|
||||
awk -v p="$progress" 'BEGIN {
|
||||
c4 = (2 * 3.14159265359) / 3;
|
||||
if (p == 0) print 0;
|
||||
else if (p == 1) print 1;
|
||||
else if (p < 0.5) print -(2 ^ (20 * p - 10) * sin((20 * p - 11.125) * c4)) / 2;
|
||||
else print (2 ^ (-20 * p + 10) * sin((20 * p - 11.125) * c4)) / 2 + 1;
|
||||
}'
|
||||
;;
|
||||
*)
|
||||
echo "$progress"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Generate keyframe distribution based on schedule type
|
||||
generate_schedule_distribution() {
|
||||
local num_frames="$1"
|
||||
local schedule_type="$2"
|
||||
local -n result_array=$3
|
||||
|
||||
case "$schedule_type" in
|
||||
uniform)
|
||||
for ((i=0; i<num_frames; i++)); do
|
||||
result_array[$i]=$(awk -v i="$i" -v n="$num_frames" 'BEGIN { print i / (n - 1) }')
|
||||
done
|
||||
;;
|
||||
front-load)
|
||||
# More frames at the beginning
|
||||
for ((i=0; i<num_frames; i++)); do
|
||||
local t=$(awk -v i="$i" -v n="$num_frames" 'BEGIN { print i / (n - 1) }')
|
||||
result_array[$i]=$(awk -v t="$t" 'BEGIN { print t * t }')
|
||||
done
|
||||
;;
|
||||
back-load)
|
||||
# More frames at the end
|
||||
for ((i=0; i<num_frames; i++)); do
|
||||
local t=$(awk -v i="$i" -v n="$num_frames" 'BEGIN { print i / (n - 1) }')
|
||||
result_array[$i]=$(awk -v t="$t" 'BEGIN { print 1 - (1 - t) * (1 - t) }')
|
||||
done
|
||||
;;
|
||||
center-peak)
|
||||
# More frames in the middle
|
||||
for ((i=0; i<num_frames; i++)); do
|
||||
local t=$(awk -v i="$i" -v n="$num_frames" 'BEGIN { print i / (n - 1) }')
|
||||
result_array[$i]=$(awk -v t="$t" 'BEGIN { print 1 - 4 * (t - 0.5) * (t - 0.5) }')
|
||||
result_array[$i]=$(awk -v val="${result_array[$i]}" -v t="$t" 'BEGIN { print t }')
|
||||
done
|
||||
;;
|
||||
edge-peak)
|
||||
# More frames at start and end
|
||||
for ((i=0; i<num_frames; i++)); do
|
||||
local t=$(awk -v i="$i" -v n="$num_frames" 'BEGIN { print i / (n - 1) }')
|
||||
result_array[$i]=$(awk -v t="$t" 'BEGIN { print 4 * (t - 0.5) * (t - 0.5) }')
|
||||
result_array[$i]=$(awk -v val="${result_array[$i]}" -v t="$t" 'BEGIN { print t }')
|
||||
done
|
||||
;;
|
||||
fibonacci)
|
||||
# Fibonacci sequence distribution
|
||||
local fib=(1 1)
|
||||
for ((i=2; i<num_frames; i++)); do
|
||||
fib[$i]=$((fib[i-1] + fib[i-2]))
|
||||
done
|
||||
local sum=0
|
||||
for val in "${fib[@]}"; do
|
||||
((sum += val))
|
||||
done
|
||||
local cumsum=0
|
||||
for ((i=0; i<num_frames; i++)); do
|
||||
((cumsum += fib[i]))
|
||||
result_array[$i]=$(awk -v c="$cumsum" -v s="$sum" 'BEGIN { print c / s }')
|
||||
done
|
||||
;;
|
||||
golden-ratio)
|
||||
# Golden ratio distribution
|
||||
local phi=1.618033988749895
|
||||
for ((i=0; i<num_frames; i++)); do
|
||||
result_array[$i]=$(awk -v i="$i" -v n="$num_frames" -v phi="$phi" 'BEGIN {
|
||||
print ((i * phi) - int(i * phi))
|
||||
}')
|
||||
done
|
||||
# Sort the array for monotonic distribution
|
||||
IFS=$'\n' result_array=($(sort -n <<<"${result_array[*]}"))
|
||||
;;
|
||||
*)
|
||||
# Default to uniform
|
||||
for ((i=0; i<num_frames; i++)); do
|
||||
result_array[$i]=$(awk -v i="$i" -v n="$num_frames" 'BEGIN { print i / (n - 1) }')
|
||||
done
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Video Processing Functions
|
||||
#############################################################################
|
||||
|
||||
get_video_duration() {
|
||||
local file="$1"
|
||||
ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$file"
|
||||
}
|
||||
|
||||
extract_frames() {
|
||||
local input="$1"
|
||||
local duration="$2"
|
||||
local -n ts_ref=$3
|
||||
local temp_dir="$4"
|
||||
|
||||
verbose_log "Extracting ${#ts_ref[@]} frames from video..."
|
||||
|
||||
for i in "${!ts_ref[@]}"; do
|
||||
local time="${ts_ref[$i]}"
|
||||
verbose_log " Frame $((i+1)): ${time}s"
|
||||
|
||||
ffmpeg -v quiet -ss "$time" -i "$input" -vframes 1 \
|
||||
-vf "scale=480:-1:flags=lanczos" \
|
||||
"${temp_dir}/frame_$(printf "%04d" "$i").png" 2>/dev/null
|
||||
done
|
||||
}
|
||||
|
||||
apply_magic_effects() {
|
||||
local magic_type="$1"
|
||||
local temp_dir="$2"
|
||||
|
||||
if [[ "$magic_type" == "none" ]]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
verbose_log "Applying magic effect: $magic_type"
|
||||
|
||||
case "$magic_type" in
|
||||
psychedelic)
|
||||
for frame in "$temp_dir"/*.png; do
|
||||
ffmpeg -v quiet -i "$frame" -vf "hue=s=3:h=sin(2*PI*t)*360" \
|
||||
"${frame}.tmp.png" 2>/dev/null && mv "${frame}.tmp.png" "$frame"
|
||||
done
|
||||
;;
|
||||
dither-bloom)
|
||||
for frame in "$temp_dir"/*.png; do
|
||||
ffmpeg -v quiet -i "$frame" -vf "format=gbrp,split[a][b],[a]negate[c],[b][c]blend=all_mode=xor,noise=alls=20:allf=t" \
|
||||
"${frame}.tmp.png" 2>/dev/null && mv "${frame}.tmp.png" "$frame"
|
||||
done
|
||||
;;
|
||||
edge-glow)
|
||||
for frame in "$temp_dir"/*.png; do
|
||||
ffmpeg -v quiet -i "$frame" -vf "edgedetect=low=0.1:high=0.3,negate,hue=s=2" \
|
||||
"${temp_dir}/edges_$(basename "$frame")"
|
||||
ffmpeg -v quiet -i "$frame" -i "${temp_dir}/edges_$(basename "$frame")" \
|
||||
-filter_complex "[0:v][1:v]blend=all_mode=addition:all_opacity=0.5" \
|
||||
"${frame}.tmp.png" 2>/dev/null && mv "${frame}.tmp.png" "$frame"
|
||||
rm "${temp_dir}/edges_$(basename "$frame")"
|
||||
done
|
||||
;;
|
||||
temporal-blur)
|
||||
# Create motion blur effect
|
||||
local frames=("$temp_dir"/*.png)
|
||||
for i in "${!frames[@]}"; do
|
||||
local prev_idx=$((i > 0 ? i - 1 : 0))
|
||||
local next_idx=$((i < ${#frames[@]} - 1 ? i + 1 : ${#frames[@]} - 1))
|
||||
|
||||
ffmpeg -v quiet -i "${frames[$prev_idx]}" -i "${frames[$i]}" -i "${frames[$next_idx]}" \
|
||||
-filter_complex "[0:v][1:v][2:v]blend=all_mode=average" \
|
||||
"${frames[$i]}.tmp.png" 2>/dev/null && mv "${frames[$i]}.tmp.png" "${frames[$i]}"
|
||||
done
|
||||
;;
|
||||
chromatic-shift)
|
||||
for frame in "$temp_dir"/*.png; do
|
||||
ffmpeg -v quiet -i "$frame" -vf "rgbashift=rh=5:bh=-5" \
|
||||
"${frame}.tmp.png" 2>/dev/null && mv "${frame}.tmp.png" "$frame"
|
||||
done
|
||||
;;
|
||||
vaporwave)
|
||||
for frame in "$temp_dir"/*.png; do
|
||||
ffmpeg -v quiet -i "$frame" -vf "curves=vintage,hue=h=300:s=1.5,eq=saturation=1.5:contrast=1.2" \
|
||||
"${frame}.tmp.png" 2>/dev/null && mv "${frame}.tmp.png" "$frame"
|
||||
done
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
create_gif() {
|
||||
local temp_dir="$1"
|
||||
local output="$2"
|
||||
local frame_delay="$3"
|
||||
|
||||
verbose_log "Creating animated GIF with ${frame_delay}ms per frame..."
|
||||
|
||||
# Convert milliseconds to centiseconds (GIF delay unit)
|
||||
local delay_cs
|
||||
delay_cs=$(awk -v ms="$frame_delay" 'BEGIN { print int(ms / 10) }')
|
||||
|
||||
# Ensure minimum delay of 1 centisecond
|
||||
if [[ $delay_cs -lt 1 ]]; then
|
||||
delay_cs=1
|
||||
fi
|
||||
|
||||
# Calculate input framerate (frames are read at this rate)
|
||||
# For GIF delay, we want 1000ms / frame_delay fps
|
||||
local fps
|
||||
fps=$(awk -v ms="$frame_delay" 'BEGIN { printf "%.2f", 1000.0 / ms }')
|
||||
|
||||
verbose_log "Frame delay: ${delay_cs} centiseconds (${frame_delay}ms), FPS: ${fps}"
|
||||
|
||||
# Generate palette for better color quality
|
||||
ffmpeg -v error -pattern_type glob -i "${temp_dir}/frame_*.png" \
|
||||
-vf "scale=480:-1:flags=lanczos,palettegen=stats_mode=diff" \
|
||||
-y "${temp_dir}/palette.png"
|
||||
|
||||
# Create GIF using palette with specified frame delay
|
||||
ffmpeg -v error -framerate "$fps" -pattern_type glob -i "${temp_dir}/frame_*.png" -i "${temp_dir}/palette.png" \
|
||||
-filter_complex "[0:v]scale=480:-1:flags=lanczos[scaled];[scaled][1:v]paletteuse=dither=bayer:bayer_scale=5" \
|
||||
-gifflags +transdiff -y "$output"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Main Processing
|
||||
#############################################################################
|
||||
|
||||
process_video() {
|
||||
local input="$INPUT_FILE"
|
||||
local output="$OUTPUT_FILE"
|
||||
|
||||
# Validate input file
|
||||
if [[ ! -f "$input" ]]; then
|
||||
log_error "Input file not found: $input"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get video duration
|
||||
local duration
|
||||
duration=$(get_video_duration "$input")
|
||||
verbose_log "Video duration: ${duration}s"
|
||||
|
||||
# Calculate schedule duration
|
||||
local schedule_duration
|
||||
schedule_duration=$(awk -v d="$duration" -v s="$INPUT_SCHEDULES" 'BEGIN { print d / s }')
|
||||
verbose_log "Schedule duration: ${schedule_duration}s (${INPUT_SCHEDULES} schedule(s))"
|
||||
|
||||
# Generate frame distribution
|
||||
local -a distribution
|
||||
generate_schedule_distribution "$KEYFRAMES" "$SCHEDULE" distribution
|
||||
|
||||
verbose_log "Using schedule: $SCHEDULE"
|
||||
verbose_log "Using transition: $TRANSITION"
|
||||
|
||||
# Calculate actual timestamps with transition function
|
||||
local -a timestamps
|
||||
for i in "${!distribution[@]}"; do
|
||||
local base_time="${distribution[$i]}"
|
||||
local weighted_time
|
||||
weighted_time=$(calculate_transition "$base_time" "$TRANSITION")
|
||||
|
||||
# Map to video duration considering input schedules
|
||||
local actual_time
|
||||
actual_time=$(awk -v w="$weighted_time" -v d="$duration" 'BEGIN { print w * d }')
|
||||
|
||||
# Ensure we don't exceed video duration
|
||||
timestamps[$i]=$(awk -v t="$actual_time" -v d="$duration" 'BEGIN {
|
||||
if (t > d) print d;
|
||||
else print t;
|
||||
}')
|
||||
done
|
||||
|
||||
# Create temporary directory
|
||||
local temp_dir
|
||||
temp_dir=$(mktemp -d)
|
||||
trap "rm -rf '$temp_dir'" EXIT
|
||||
|
||||
# Extract frames
|
||||
extract_frames "$input" "$duration" timestamps "$temp_dir"
|
||||
|
||||
# Apply magic effects
|
||||
apply_magic_effects "$MAGIC" "$temp_dir"
|
||||
|
||||
# Create GIF with specified frame duration
|
||||
create_gif "$temp_dir" "$output" "$KEYFRAME_DURATION"
|
||||
|
||||
log_success "GIF created successfully: $output"
|
||||
|
||||
# Show file size
|
||||
local size
|
||||
size=$(du -h "$output" | cut -f1)
|
||||
log_info "Output size: $size"
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Command Line Parsing
|
||||
#############################################################################
|
||||
|
||||
parse_arguments() {
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-k|--keyframes)
|
||||
KEYFRAMES="$2"
|
||||
shift 2
|
||||
;;
|
||||
-d|--keyframe-duration)
|
||||
KEYFRAME_DURATION="$2"
|
||||
shift 2
|
||||
;;
|
||||
-i|--input-schedules)
|
||||
INPUT_SCHEDULES="$2"
|
||||
shift 2
|
||||
;;
|
||||
-t|--transition)
|
||||
TRANSITION="$2"
|
||||
if ! validate_enum "$TRANSITION" TRANSITIONS; then
|
||||
log_error "Invalid transition type: $TRANSITION"
|
||||
log_error "Available: ${TRANSITIONS[*]}"
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
-s|--schedule)
|
||||
SCHEDULE="$2"
|
||||
if ! validate_enum "$SCHEDULE" SCHEDULES; then
|
||||
log_error "Invalid schedule type: $SCHEDULE"
|
||||
log_error "Available: ${SCHEDULES[*]}"
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
-m|--magic)
|
||||
MAGIC="$2"
|
||||
if ! validate_enum "$MAGIC" MAGICS; then
|
||||
log_error "Invalid magic type: $MAGIC"
|
||||
log_error "Available: ${MAGICS[*]}"
|
||||
exit 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
-v|--verbose)
|
||||
VERBOSE=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
print_usage
|
||||
exit 0
|
||||
;;
|
||||
-*)
|
||||
log_error "Unknown option: $1"
|
||||
print_usage
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
if [[ -z "$INPUT_FILE" ]]; then
|
||||
INPUT_FILE="$1"
|
||||
elif [[ -z "$OUTPUT_FILE" ]]; then
|
||||
OUTPUT_FILE="$1"
|
||||
else
|
||||
log_error "Too many arguments"
|
||||
print_usage
|
||||
exit 1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate required arguments
|
||||
if [[ -z "$INPUT_FILE" ]]; then
|
||||
log_error "Input file is required"
|
||||
print_usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set default output file
|
||||
if [[ -z "$OUTPUT_FILE" ]]; then
|
||||
OUTPUT_FILE="${INPUT_FILE%.*}.gif"
|
||||
fi
|
||||
|
||||
# Validate numeric arguments
|
||||
if ! [[ "$KEYFRAMES" =~ ^[0-9]+$ ]] || [[ "$KEYFRAMES" -lt 2 ]]; then
|
||||
log_error "Keyframes must be a positive integer >= 2"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ "$KEYFRAME_DURATION" =~ ^[0-9]+$ ]] || [[ "$KEYFRAME_DURATION" -lt 1 ]] || [[ "$KEYFRAME_DURATION" -gt 30000 ]]; then
|
||||
log_error "Keyframe duration must be an integer between 1 and 30000 milliseconds"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ "$INPUT_SCHEDULES" =~ ^[0-9]+$ ]] || [[ "$INPUT_SCHEDULES" -lt 1 ]]; then
|
||||
log_error "Input schedules must be a positive integer >= 1"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
#############################################################################
|
||||
# Entry Point
|
||||
#############################################################################
|
||||
|
||||
main() {
|
||||
parse_arguments "$@"
|
||||
|
||||
log_info "Starting MP4 to GIF conversion..."
|
||||
log_info "Configuration:"
|
||||
log_info " Input: $INPUT_FILE"
|
||||
log_info " Output: $OUTPUT_FILE"
|
||||
log_info " Keyframes: $KEYFRAMES"
|
||||
log_info " Frame Duration: ${KEYFRAME_DURATION}ms"
|
||||
log_info " Schedules: $INPUT_SCHEDULES"
|
||||
log_info " Transition: $TRANSITION"
|
||||
log_info " Schedule: $SCHEDULE"
|
||||
log_info " Magic: $MAGIC"
|
||||
|
||||
process_video
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
13
.init/eval.sh
Executable file
13
.init/eval.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
if command -v oh-my-posh 2>&1 >/dev/null; then
|
||||
eval "$(! oh-my-posh init zsh --config=~/worker.omp.json)"
|
||||
fi
|
||||
|
||||
if command -v rbenv 2>&1 >/dev/null; then
|
||||
eval "$(rbenv init - --no-rehash zsh)"
|
||||
fi
|
||||
|
||||
if command -v pyenv 2>&1 >/dev/null; then
|
||||
eval "$(pyenv init --path)"
|
||||
fi
|
||||
5
.init/export.sh
Executable file
5
.init/export.sh
Executable file
@@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
|
||||
export NVM_DIR="$HOME/.nvm"
|
||||
export REPOS_DIR="$HOME/repos"
|
||||
export CHORE_CHORE="chore: chore"
|
||||
116
.init/functions.sh
Executable file
116
.init/functions.sh
Executable file
@@ -0,0 +1,116 @@
|
||||
#!/bin/bash
|
||||
|
||||
|
||||
is_ssh() {
|
||||
IS_SSH=$(cat /proc/$PPID/status | head -1 | cut -f2)
|
||||
if [ "$_IS_SSH" = "sshd-session" ]; then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
_home_push() {
|
||||
git add -A
|
||||
git commit -m "${1:-$CHORE_CHORE}"
|
||||
git push $2 $3
|
||||
}
|
||||
|
||||
_home_pull() {
|
||||
[ -n $(is_ssh) ] && git checkout $HOME/.last_pwd
|
||||
git stash
|
||||
git pull $1 $2
|
||||
git stash pop
|
||||
}
|
||||
|
||||
_site_deploy_jekyll() {
|
||||
cd "$HOME/repos/$1"
|
||||
rm -rf _site
|
||||
JEKYLL_ENV=production bundle exec jekyll build
|
||||
rsync -avzhe ssh _site/ "pi@hive:$DOCKER_STORAGE_DIR/staticwebserver/hosts/$1/" --delete
|
||||
cd -
|
||||
}
|
||||
|
||||
_site_deploy_nuxt() {
|
||||
cd "$HOME/repos/$1"
|
||||
rm -rf .output
|
||||
npm run generate
|
||||
rsync -avzhe ssh .output/public/ "pi@hive:$DOCKER_STORAGE_DIR/staticwebserver/hosts/$1/" --delete
|
||||
cd -
|
||||
}
|
||||
|
||||
_site_deploy_static() {
|
||||
cd "$HOME/repos/$1"
|
||||
rsync -avzhe ssh src/ "pi@hive:$DOCKER_STORAGE_DIR/staticwebserver/hosts/$1/" --delete
|
||||
cd -
|
||||
}
|
||||
|
||||
_site_run_jekyll() {
|
||||
cd "$HOME/repos/$1"
|
||||
bundle exec jekyll serve --livereload
|
||||
cd -
|
||||
}
|
||||
|
||||
_site_run_nuxt() {
|
||||
cd "$HOME/repos/$1"
|
||||
npm run dev
|
||||
cd -
|
||||
}
|
||||
|
||||
_site_run_static() {
|
||||
cd "$HOME/repos/$1"
|
||||
python -m http.server 8000 -d src
|
||||
cd -
|
||||
}
|
||||
|
||||
batch_file_sequence() {
|
||||
a=0
|
||||
for i in *.$2; do
|
||||
new=$(printf "$1-%03d.$2" "$a")
|
||||
mv -i -- "$i" "$new"
|
||||
let a="$a+1"
|
||||
done
|
||||
}
|
||||
|
||||
batch_image_webp() {
|
||||
find . -type f -regex ".*\.\(jpg\|jpeg\|png\)" -exec mogrify -format webp {} \; -print
|
||||
find . -type f -regex ".*\.\(jpg\|jpeg\|png\)" -exec rm {} \; -print
|
||||
}
|
||||
|
||||
batch_video_x264() {
|
||||
find . -type f -regex ".*\.\(mp4\)" -exec ffmpeg -i {} -vcodec libx264 -crf 24 "{}.mp4" \; -print
|
||||
find . -type f -regex ".+mp4\.mp4" -exec python3 -c "import os;os.rename('{}', '{}'[:-4])" \; -print
|
||||
}
|
||||
|
||||
_image_description() {
|
||||
identify -verbose $1 | grep ImageDescription | sed "s/ exif:ImageDescription: //"
|
||||
}
|
||||
|
||||
_image_optimize() {
|
||||
i_x4 && cp -rf x4/* . && i_x05 && cp -rf x05/* . && _file_sequence $1 webp && mv $1-000.webp $1.webp
|
||||
_image_description *.png
|
||||
rm -rf *.png x4 x05
|
||||
}
|
||||
|
||||
_video_optimize() {
|
||||
filename=$(basename -- "$1")
|
||||
extension="${filename##*.}"
|
||||
filename="${filename%.*}"
|
||||
ffmpeg -y -i $1 -vf "setpts=1.25*PTS" -r 24 "$filename.mp4"
|
||||
}
|
||||
|
||||
function _over_subdirs {
|
||||
_PWD=$PWD
|
||||
. $PWD/.env
|
||||
for D in $(find . -mindepth 1 -maxdepth 1 -type d); do
|
||||
cd "$D" && $1
|
||||
cd $_PWD
|
||||
done
|
||||
}
|
||||
|
||||
_join_by() {
|
||||
local d=${1-} f=${2-}
|
||||
if shift 2; then
|
||||
printf %s "$f" "${@/#/$d}"
|
||||
fi
|
||||
}
|
||||
0
.init/hooks/.gitkeep
Normal file
0
.init/hooks/.gitkeep
Normal file
41
.init/init.sh
Executable file
41
.init/init.sh
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -f "$HOME/.init/path.sh" ] ; then
|
||||
. "$HOME/.init/path.sh"
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.init/export.sh" ] ; then
|
||||
. "$HOME/.init/export.sh"
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.init/alias.sh" ] ; then
|
||||
. "$HOME/.init/alias.sh"
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.init/source.sh" ] ; then
|
||||
. "$HOME/.init/source.sh"
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.init/functions.sh" ] ; then
|
||||
. "$HOME/.init/functions.sh"
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.init/links.sh" ] ; then
|
||||
. "$HOME/.init/links.sh"
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.init/source.sh" ] ; then
|
||||
. "$HOME/.init/source.sh"
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.init/eval.sh" ] ; then
|
||||
. "$HOME/.init/eval.sh"
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.init/trap.sh" ] ; then
|
||||
. "$HOME/.init/trap.sh"
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.init/start.sh" ] ; then
|
||||
. "$HOME/.init/start.sh"
|
||||
fi
|
||||
1
.init/links.sh
Executable file
1
.init/links.sh
Executable file
@@ -0,0 +1 @@
|
||||
#!/bin/bash
|
||||
66
.init/path.sh
Executable file
66
.init/path.sh
Executable file
@@ -0,0 +1,66 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -d "$HOME/bin" ]; then
|
||||
export PATH="$HOME/bin:$PATH"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.local/bin" ]; then
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.rvm/bin" ]; then
|
||||
export PATH="$HOME/.rvm/bin:$PATH"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/repos/flutter/bin" ]; then
|
||||
export PATH="$HOME/repos/flutter/bin:$PATH"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.rbenv/bin" ]; then
|
||||
export PATH="$PATH:$HOME/.rbenv/bin"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.pyenv/bin" ]; then
|
||||
export PYENV_ROOT="$HOME/.pyenv"
|
||||
export PATH="$PYENV_ROOT/bin:$PATH"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.cargo/bin" ]; then
|
||||
export PATH="$PATH:$HOME/.cargo/bin"
|
||||
fi
|
||||
|
||||
if [ -d "/opt/Upscayl/resources/bin" ]; then
|
||||
export PATH="$PATH:/opt/Upscayl/resources/bin"
|
||||
fi
|
||||
|
||||
if [ -d "/usr/local/go/bin" ]; then
|
||||
export PATH="$PATH:/usr/local/go/bin"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/go/bin" ]; then
|
||||
export PATH="$PATH:$HOME/go/bin"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/node_modules/.bin" ]; then
|
||||
export PATH="$PATH:$HOME/node_modules/.bin"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/miniconda3/bin" ]; then
|
||||
export PATH="$PATH:$HOME/miniconda3/bin"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.local/share/flatpak/exports/share" ] ; then
|
||||
export XDG_DATA_DIRS="$XDG_DATA_DIRS:$HOME/.local/share/flatpak/exports/share"
|
||||
fi
|
||||
|
||||
if [ -d "/var/lib/flatpak/exports/share" ] ; then
|
||||
export XDG_DATA_DIRS="$XDG_DATA_DIRS:/var/lib/flatpak/exports/share"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/.init/bin" ] ; then
|
||||
export PATH="$PATH:$HOME/.init/bin"
|
||||
fi
|
||||
|
||||
if [ -d "$HOME/Projects/kompose" ] ; then
|
||||
export PATH="$PATH:$HOME/Projects/kompose"
|
||||
fi
|
||||
28
.init/source.sh
Executable file
28
.init/source.sh
Executable file
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ -n "$BASH_VERSION" ]; then
|
||||
# include .bashrc if it exists
|
||||
if [ -f "$HOME/.bashrc" ]; then
|
||||
. "$HOME/.bashrc"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -s "$NVM_DIR/nvm.sh" ] ; then
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
fi
|
||||
|
||||
if [ -s "$NVM_DIR/bash_completion" ] ; then
|
||||
. "$NVM_DIR/bash_completion"
|
||||
fi
|
||||
|
||||
if [ -s "$HOME/.rvm/scripts/rvm" ] ; then
|
||||
. "$HOME/.rvm/scripts/rvm"
|
||||
fi
|
||||
|
||||
if [ -s "$HOME/.cargo/env" ] ; then
|
||||
. "$HOME/.cargo/env"
|
||||
fi
|
||||
|
||||
# if [ -s "$HOME/.gvm/scripts/gvm" ]; then
|
||||
# . "$HOME/.gvm/scripts/gvm"
|
||||
# fi
|
||||
26
.init/start.sh
Executable file
26
.init/start.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
ssh-add &>/dev/null
|
||||
|
||||
# SSH_ENV="$HOME/.ssh/agent-environment"
|
||||
|
||||
# function start_agent {
|
||||
# echo "Initialising new SSH agent..."
|
||||
# /usr/bin/ssh-agent | sed 's/^echo/#echo/' >"$SSH_ENV"
|
||||
# echo succeeded
|
||||
# chmod 600 "$SSH_ENV"
|
||||
# . "$SSH_ENV" >/dev/null
|
||||
# /usr/bin/ssh-add;
|
||||
# }
|
||||
|
||||
# # Source SSH settings, if applicable
|
||||
|
||||
# if [ -f "$SSH_ENV" ]; then
|
||||
# . "$SSH_ENV" >/dev/null
|
||||
# #ps $SSH_AGENT_PID doesn't work under Cygwin
|
||||
# ps -ef | grep $SSH_AGENT_PID | grep ssh-agent$ >/dev/null || {
|
||||
# start_agent
|
||||
# }
|
||||
# else
|
||||
# start_agent
|
||||
# fi
|
||||
13
.init/trap.sh
Executable file
13
.init/trap.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
TRAPINT() {
|
||||
}
|
||||
|
||||
TRAPQUIT() {
|
||||
}
|
||||
|
||||
TRAPTERM() {
|
||||
}
|
||||
|
||||
TRAPEXIT() {
|
||||
}
|
||||
193
.p10k.zsh
Normal file
193
.p10k.zsh
Normal file
@@ -0,0 +1,193 @@
|
||||
# Generated by Powerlevel10k configuration wizard on 2025-09-04 at 02:33 CEST.
|
||||
# Based on romkatv/powerlevel10k/config/p10k-pure.zsh, checksum 07533.
|
||||
# Wizard options: nerdfont-v3 + powerline, small icons, pure, snazzy, 24h time, 1 line,
|
||||
# compact, instant_prompt=verbose.
|
||||
# Type `p10k configure` to generate another config.
|
||||
#
|
||||
# Config file for Powerlevel10k with the style of Pure (https://github.com/sindresorhus/pure).
|
||||
#
|
||||
# Differences from Pure:
|
||||
#
|
||||
# - Git:
|
||||
# - `@c4d3ec2c` instead of something like `v1.4.0~11` when in detached HEAD state.
|
||||
# - No automatic `git fetch` (the same as in Pure with `PURE_GIT_PULL=0`).
|
||||
#
|
||||
# Apart from the differences listed above, the replication of Pure prompt is exact. This includes
|
||||
# even the questionable parts. For example, just like in Pure, there is no indication of Git status
|
||||
# being stale; prompt symbol is the same in command, visual and overwrite vi modes; when prompt
|
||||
# doesn't fit on one line, it wraps around with no attempt to shorten it.
|
||||
#
|
||||
# If you like the general style of Pure but not particularly attached to all its quirks, type
|
||||
# `p10k configure` and pick "Lean" style. This will give you slick minimalist prompt while taking
|
||||
# advantage of Powerlevel10k features that aren't present in Pure.
|
||||
|
||||
# Temporarily change options.
|
||||
'builtin' 'local' '-a' 'p10k_config_opts'
|
||||
[[ ! -o 'aliases' ]] || p10k_config_opts+=('aliases')
|
||||
[[ ! -o 'sh_glob' ]] || p10k_config_opts+=('sh_glob')
|
||||
[[ ! -o 'no_brace_expand' ]] || p10k_config_opts+=('no_brace_expand')
|
||||
'builtin' 'setopt' 'no_aliases' 'no_sh_glob' 'brace_expand'
|
||||
|
||||
() {
|
||||
emulate -L zsh -o extended_glob
|
||||
|
||||
# Unset all configuration options.
|
||||
unset -m '(POWERLEVEL9K_*|DEFAULT_USER)~POWERLEVEL9K_GITSTATUS_DIR'
|
||||
|
||||
# Zsh >= 5.1 is required.
|
||||
[[ $ZSH_VERSION == (5.<1->*|<6->.*) ]] || return
|
||||
|
||||
# Prompt colors.
|
||||
local grey='242'
|
||||
local red='#FF5C57'
|
||||
local yellow='#F3F99D'
|
||||
local blue='#57C7FF'
|
||||
local magenta='#FF6AC1'
|
||||
local cyan='#9AEDFE'
|
||||
local white='#F1F1F0'
|
||||
|
||||
# Left prompt segments.
|
||||
typeset -g POWERLEVEL9K_LEFT_PROMPT_ELEMENTS=(
|
||||
context # user@host
|
||||
dir # current directory
|
||||
vcs # git status
|
||||
command_execution_time # previous command duration
|
||||
# virtualenv # python virtual environment
|
||||
prompt_char # prompt symbol
|
||||
)
|
||||
|
||||
# Right prompt segments.
|
||||
typeset -g POWERLEVEL9K_RIGHT_PROMPT_ELEMENTS=(
|
||||
# command_execution_time # previous command duration
|
||||
# virtualenv # python virtual environment
|
||||
# context # user@host
|
||||
time # current time
|
||||
)
|
||||
|
||||
# Basic style options that define the overall prompt look.
|
||||
typeset -g POWERLEVEL9K_BACKGROUND= # transparent background
|
||||
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_{LEFT,RIGHT}_WHITESPACE= # no surrounding whitespace
|
||||
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SUBSEGMENT_SEPARATOR=' ' # separate segments with a space
|
||||
typeset -g POWERLEVEL9K_{LEFT,RIGHT}_SEGMENT_SEPARATOR= # no end-of-line symbol
|
||||
typeset -g POWERLEVEL9K_VISUAL_IDENTIFIER_EXPANSION= # no segment icons
|
||||
|
||||
# Add an empty line before each prompt except the first. This doesn't emulate the bug
|
||||
# in Pure that makes prompt drift down whenever you use the Alt-C binding from fzf or similar.
|
||||
typeset -g POWERLEVEL9K_PROMPT_ADD_NEWLINE=false
|
||||
|
||||
# Magenta prompt symbol if the last command succeeded.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_OK_{VIINS,VICMD,VIVIS}_FOREGROUND=$magenta
|
||||
# Red prompt symbol if the last command failed.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_ERROR_{VIINS,VICMD,VIVIS}_FOREGROUND=$red
|
||||
# Default prompt symbol.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIINS_CONTENT_EXPANSION='❯'
|
||||
# Prompt symbol in command vi mode.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VICMD_CONTENT_EXPANSION='❮'
|
||||
# Prompt symbol in visual vi mode is the same as in command mode.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_{OK,ERROR}_VIVIS_CONTENT_EXPANSION='❮'
|
||||
# Prompt symbol in overwrite vi mode is the same as in command mode.
|
||||
typeset -g POWERLEVEL9K_PROMPT_CHAR_OVERWRITE_STATE=false
|
||||
|
||||
# Grey Python Virtual Environment.
|
||||
typeset -g POWERLEVEL9K_VIRTUALENV_FOREGROUND=$grey
|
||||
# Don't show Python version.
|
||||
typeset -g POWERLEVEL9K_VIRTUALENV_SHOW_PYTHON_VERSION=false
|
||||
typeset -g POWERLEVEL9K_VIRTUALENV_{LEFT,RIGHT}_DELIMITER=
|
||||
|
||||
# Blue current directory.
|
||||
typeset -g POWERLEVEL9K_DIR_FOREGROUND=$blue
|
||||
|
||||
# Context format when root: user@host. The first part white, the rest grey.
|
||||
typeset -g POWERLEVEL9K_CONTEXT_ROOT_TEMPLATE="%F{$white}%n%f%F{$grey}@%m%f"
|
||||
# Context format when not root: user@host. The whole thing grey.
|
||||
typeset -g POWERLEVEL9K_CONTEXT_TEMPLATE="%F{$grey}%n@%m%f"
|
||||
# Don't show context unless root or in SSH.
|
||||
typeset -g POWERLEVEL9K_CONTEXT_{DEFAULT,SUDO}_CONTENT_EXPANSION=
|
||||
|
||||
# Show previous command duration only if it's >= 5s.
|
||||
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_THRESHOLD=5
|
||||
# Don't show fractional seconds. Thus, 7s rather than 7.3s.
|
||||
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_PRECISION=0
|
||||
# Duration format: 1d 2h 3m 4s.
|
||||
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FORMAT='d h m s'
|
||||
# Yellow previous command duration.
|
||||
typeset -g POWERLEVEL9K_COMMAND_EXECUTION_TIME_FOREGROUND=$yellow
|
||||
|
||||
# Grey Git prompt. This makes stale prompts indistinguishable from up-to-date ones.
|
||||
typeset -g POWERLEVEL9K_VCS_FOREGROUND=$grey
|
||||
|
||||
# Disable async loading indicator to make directories that aren't Git repositories
|
||||
# indistinguishable from large Git repositories without known state.
|
||||
typeset -g POWERLEVEL9K_VCS_LOADING_TEXT=
|
||||
|
||||
# Don't wait for Git status even for a millisecond, so that prompt always updates
|
||||
# asynchronously when Git state changes.
|
||||
typeset -g POWERLEVEL9K_VCS_MAX_SYNC_LATENCY_SECONDS=0
|
||||
|
||||
# Cyan ahead/behind arrows.
|
||||
typeset -g POWERLEVEL9K_VCS_{INCOMING,OUTGOING}_CHANGESFORMAT_FOREGROUND=$cyan
|
||||
# Don't show remote branch, current tag or stashes.
|
||||
typeset -g POWERLEVEL9K_VCS_GIT_HOOKS=(vcs-detect-changes git-untracked git-aheadbehind)
|
||||
# Don't show the branch icon.
|
||||
typeset -g POWERLEVEL9K_VCS_BRANCH_ICON=
|
||||
# When in detached HEAD state, show @commit where branch normally goes.
|
||||
typeset -g POWERLEVEL9K_VCS_COMMIT_ICON='@'
|
||||
# Don't show staged, unstaged, untracked indicators.
|
||||
typeset -g POWERLEVEL9K_VCS_{STAGED,UNSTAGED,UNTRACKED}_ICON=
|
||||
# Show '*' when there are staged, unstaged or untracked files.
|
||||
typeset -g POWERLEVEL9K_VCS_DIRTY_ICON='*'
|
||||
# Show '⇣' if local branch is behind remote.
|
||||
typeset -g POWERLEVEL9K_VCS_INCOMING_CHANGES_ICON=':⇣'
|
||||
# Show '⇡' if local branch is ahead of remote.
|
||||
typeset -g POWERLEVEL9K_VCS_OUTGOING_CHANGES_ICON=':⇡'
|
||||
# Don't show the number of commits next to the ahead/behind arrows.
|
||||
typeset -g POWERLEVEL9K_VCS_{COMMITS_AHEAD,COMMITS_BEHIND}_MAX_NUM=1
|
||||
# Remove space between '⇣' and '⇡' and all trailing spaces.
|
||||
typeset -g POWERLEVEL9K_VCS_CONTENT_EXPANSION='${${${P9K_CONTENT/⇣* :⇡/⇣⇡}// }//:/ }'
|
||||
|
||||
# Grey current time.
|
||||
typeset -g POWERLEVEL9K_TIME_FOREGROUND=$grey
|
||||
# Format for the current time: 09:51:02. See `man 3 strftime`.
|
||||
typeset -g POWERLEVEL9K_TIME_FORMAT='%D{%H:%M:%S}'
|
||||
# If set to true, time will update when you hit enter. This way prompts for the past
|
||||
# commands will contain the start times of their commands rather than the end times of
|
||||
# their preceding commands.
|
||||
typeset -g POWERLEVEL9K_TIME_UPDATE_ON_COMMAND=false
|
||||
|
||||
# Transient prompt works similarly to the builtin transient_rprompt option. It trims down prompt
|
||||
# when accepting a command line. Supported values:
|
||||
#
|
||||
# - off: Don't change prompt when accepting a command line.
|
||||
# - always: Trim down prompt when accepting a command line.
|
||||
# - same-dir: Trim down prompt when accepting a command line unless this is the first command
|
||||
# typed after changing current working directory.
|
||||
typeset -g POWERLEVEL9K_TRANSIENT_PROMPT=off
|
||||
|
||||
# Instant prompt mode.
|
||||
#
|
||||
# - off: Disable instant prompt. Choose this if you've tried instant prompt and found
|
||||
# it incompatible with your zsh configuration files.
|
||||
# - quiet: Enable instant prompt and don't print warnings when detecting console output
|
||||
# during zsh initialization. Choose this if you've read and understood
|
||||
# https://github.com/romkatv/powerlevel10k#instant-prompt.
|
||||
# - verbose: Enable instant prompt and print a warning when detecting console output during
|
||||
# zsh initialization. Choose this if you've never tried instant prompt, haven't
|
||||
# seen the warning, or if you are unsure what this all means.
|
||||
typeset -g POWERLEVEL9K_INSTANT_PROMPT=off
|
||||
|
||||
# Hot reload allows you to change POWERLEVEL9K options after Powerlevel10k has been initialized.
|
||||
# For example, you can type POWERLEVEL9K_BACKGROUND=red and see your prompt turn red. Hot reload
|
||||
# can slow down prompt by 1-2 milliseconds, so it's better to keep it turned off unless you
|
||||
# really need it.
|
||||
typeset -g POWERLEVEL9K_DISABLE_HOT_RELOAD=off
|
||||
|
||||
# If p10k is already loaded, reload configuration.
|
||||
# This works even with POWERLEVEL9K_DISABLE_HOT_RELOAD=true.
|
||||
(( ! $+functions[p10k] )) || p10k reload
|
||||
}
|
||||
|
||||
# Tell `p10k configure` which file it should overwrite.
|
||||
typeset -g POWERLEVEL9K_CONFIG_FILE=${${(%):-%x}:a}
|
||||
|
||||
(( ${#p10k_config_opts} )) && setopt ${p10k_config_opts[@]}
|
||||
'builtin' 'unset' 'p10k_config_opts'
|
||||
5
.pre-commit-config.yaml
Normal file
5
.pre-commit-config.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
fail_fast: false
|
||||
default_stages: [pre-commit]
|
||||
repos:
|
||||
- repo: local
|
||||
hooks: []
|
||||
5
.prettierignore
Normal file
5
.prettierignore
Normal file
@@ -0,0 +1,5 @@
|
||||
# Ignore artifacts:
|
||||
build
|
||||
coverage
|
||||
|
||||
repos/hydejack/**/*.html
|
||||
16
.prettierrc
Normal file
16
.prettierrc
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"arrowParens": "always",
|
||||
"bracketSpacing": true,
|
||||
"htmlWhitespaceSensitivity": "css",
|
||||
"insertPragma": false,
|
||||
"jsxSingleQuote": true,
|
||||
"printWidth": 80,
|
||||
"proseWrap": "always",
|
||||
"quoteProps": "as-needed",
|
||||
"requirePragma": false,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 2,
|
||||
"trailingComma": "none",
|
||||
"useTabs": false
|
||||
}
|
||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
||||
3.10.17
|
||||
267
.rubocop.yml
Normal file
267
.rubocop.yml
Normal file
@@ -0,0 +1,267 @@
|
||||
AllCops:
|
||||
NewCops: enable
|
||||
|
||||
Gemspec/AddRuntimeDependency: # new in 1.65
|
||||
Enabled: true
|
||||
Gemspec/DeprecatedAttributeAssignment: # new in 1.30
|
||||
Enabled: true
|
||||
Gemspec/DevelopmentDependencies: # new in 1.44
|
||||
Enabled: true
|
||||
Gemspec/RequireMFA: # new in 1.23
|
||||
Enabled: true
|
||||
Layout/LineContinuationLeadingSpace: # new in 1.31
|
||||
Enabled: true
|
||||
Layout/LineContinuationSpacing: # new in 1.31
|
||||
Enabled: true
|
||||
Layout/LineEndStringConcatenationIndentation: # new in 1.18
|
||||
Enabled: true
|
||||
Layout/SpaceBeforeBrackets: # new in 1.7
|
||||
Enabled: true
|
||||
Lint/AmbiguousAssignment: # new in 1.7
|
||||
Enabled: true
|
||||
Lint/AmbiguousOperatorPrecedence: # new in 1.21
|
||||
Enabled: true
|
||||
Lint/AmbiguousRange: # new in 1.19
|
||||
Enabled: true
|
||||
Lint/ArrayLiteralInRegexp: # new in 1.71
|
||||
Enabled: true
|
||||
Lint/ConstantOverwrittenInRescue: # new in 1.31
|
||||
Enabled: true
|
||||
Lint/ConstantReassignment: # new in 1.70
|
||||
Enabled: true
|
||||
Lint/CopDirectiveSyntax: # new in 1.72
|
||||
Enabled: true
|
||||
Lint/DeprecatedConstants: # new in 1.8
|
||||
Enabled: true
|
||||
Lint/DuplicateBranch: # new in 1.3
|
||||
Enabled: true
|
||||
Lint/DuplicateMagicComment: # new in 1.37
|
||||
Enabled: true
|
||||
Lint/DuplicateMatchPattern: # new in 1.50
|
||||
Enabled: true
|
||||
Lint/DuplicateRegexpCharacterClassElement: # new in 1.1
|
||||
Enabled: true
|
||||
Lint/DuplicateSetElement: # new in 1.67
|
||||
Enabled: true
|
||||
Lint/EmptyBlock: # new in 1.1
|
||||
Enabled: true
|
||||
Lint/EmptyClass: # new in 1.3
|
||||
Enabled: true
|
||||
Lint/EmptyInPattern: # new in 1.16
|
||||
Enabled: true
|
||||
Lint/HashNewWithKeywordArgumentsAsDefault: # new in 1.69
|
||||
Enabled: true
|
||||
Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21
|
||||
Enabled: true
|
||||
Lint/ItWithoutArgumentsInBlock: # new in 1.59
|
||||
Enabled: true
|
||||
Lint/LambdaWithoutLiteralBlock: # new in 1.8
|
||||
Enabled: true
|
||||
Lint/LiteralAssignmentInCondition: # new in 1.58
|
||||
Enabled: true
|
||||
Lint/MixedCaseRange: # new in 1.53
|
||||
Enabled: true
|
||||
Lint/NoReturnInBeginEndBlocks: # new in 1.2
|
||||
Enabled: true
|
||||
Lint/NonAtomicFileOperation: # new in 1.31
|
||||
Enabled: true
|
||||
Lint/NumberedParameterAssignment: # new in 1.9
|
||||
Enabled: true
|
||||
Lint/NumericOperationWithConstantResult: # new in 1.69
|
||||
Enabled: true
|
||||
Lint/OrAssignmentToConstant: # new in 1.9
|
||||
Enabled: true
|
||||
Lint/RedundantDirGlobSort: # new in 1.8
|
||||
Enabled: true
|
||||
Lint/RedundantRegexpQuantifiers: # new in 1.53
|
||||
Enabled: true
|
||||
Lint/RedundantTypeConversion: # new in 1.72
|
||||
Enabled: true
|
||||
Lint/RefinementImportMethods: # new in 1.27
|
||||
Enabled: true
|
||||
Lint/RequireRangeParentheses: # new in 1.32
|
||||
Enabled: true
|
||||
Lint/RequireRelativeSelfPath: # new in 1.22
|
||||
Enabled: true
|
||||
Lint/SharedMutableDefault: # new in 1.70
|
||||
Enabled: true
|
||||
Lint/SuppressedExceptionInNumberConversion: # new in 1.72
|
||||
Enabled: true
|
||||
Lint/SymbolConversion: # new in 1.9
|
||||
Enabled: true
|
||||
Lint/ToEnumArguments: # new in 1.1
|
||||
Enabled: true
|
||||
Lint/TripleQuotes: # new in 1.9
|
||||
Enabled: true
|
||||
Lint/UnescapedBracketInRegexp: # new in 1.68
|
||||
Enabled: true
|
||||
Lint/UnexpectedBlockArity: # new in 1.5
|
||||
Enabled: true
|
||||
Lint/UnmodifiedReduceAccumulator: # new in 1.1
|
||||
Enabled: true
|
||||
Lint/UselessConstantScoping: # new in 1.72
|
||||
Enabled: true
|
||||
Lint/UselessDefined: # new in 1.69
|
||||
Enabled: true
|
||||
Lint/UselessNumericOperation: # new in 1.66
|
||||
Enabled: true
|
||||
Lint/UselessRescue: # new in 1.43
|
||||
Enabled: true
|
||||
Lint/UselessRuby2Keywords: # new in 1.23
|
||||
Enabled: true
|
||||
Metrics/CollectionLiteralLength: # new in 1.47
|
||||
Enabled: true
|
||||
Naming/BlockForwarding: # new in 1.24
|
||||
Enabled: true
|
||||
Security/CompoundHash: # new in 1.28
|
||||
Enabled: true
|
||||
Security/IoMethods: # new in 1.22
|
||||
Enabled: true
|
||||
Style/AmbiguousEndlessMethodDefinition: # new in 1.68
|
||||
Enabled: true
|
||||
Style/ArgumentsForwarding: # new in 1.1
|
||||
Enabled: true
|
||||
Style/ArrayIntersect: # new in 1.40
|
||||
Enabled: true
|
||||
Style/BitwisePredicate: # new in 1.68
|
||||
Enabled: true
|
||||
Style/CollectionCompact: # new in 1.2
|
||||
Enabled: true
|
||||
Style/CombinableDefined: # new in 1.68
|
||||
Enabled: true
|
||||
Style/ComparableBetween: # new in 1.74
|
||||
Enabled: true
|
||||
Style/ComparableClamp: # new in 1.44
|
||||
Enabled: true
|
||||
Style/ConcatArrayLiterals: # new in 1.41
|
||||
Enabled: true
|
||||
Style/DataInheritance: # new in 1.49
|
||||
Enabled: true
|
||||
Style/DigChain: # new in 1.69
|
||||
Enabled: true
|
||||
Style/DirEmpty: # new in 1.48
|
||||
Enabled: true
|
||||
Style/DocumentDynamicEvalDefinition: # new in 1.1
|
||||
Enabled: true
|
||||
Style/EmptyHeredoc: # new in 1.32
|
||||
Enabled: true
|
||||
Style/EndlessMethod: # new in 1.8
|
||||
Enabled: true
|
||||
Style/EnvHome: # new in 1.29
|
||||
Enabled: true
|
||||
Style/ExactRegexpMatch: # new in 1.51
|
||||
Enabled: true
|
||||
Style/FetchEnvVar: # new in 1.28
|
||||
Enabled: true
|
||||
Style/FileEmpty: # new in 1.48
|
||||
Enabled: true
|
||||
Style/FileNull: # new in 1.69
|
||||
Enabled: true
|
||||
Style/FileRead: # new in 1.24
|
||||
Enabled: true
|
||||
Style/FileTouch: # new in 1.69
|
||||
Enabled: true
|
||||
Style/FileWrite: # new in 1.24
|
||||
Enabled: true
|
||||
Style/HashConversion: # new in 1.10
|
||||
Enabled: true
|
||||
Style/HashExcept: # new in 1.7
|
||||
Enabled: true
|
||||
Style/HashFetchChain: # new in 1.75
|
||||
Enabled: true
|
||||
Style/HashSlice: # new in 1.71
|
||||
Enabled: true
|
||||
Style/IfWithBooleanLiteralBranches: # new in 1.9
|
||||
Enabled: true
|
||||
Style/InPatternThen: # new in 1.16
|
||||
Enabled: true
|
||||
Style/ItAssignment: # new in 1.70
|
||||
Enabled: true
|
||||
Style/ItBlockParameter: # new in 1.75
|
||||
Enabled: true
|
||||
Style/KeywordArgumentsMerging: # new in 1.68
|
||||
Enabled: true
|
||||
Style/MagicCommentFormat: # new in 1.35
|
||||
Enabled: true
|
||||
Style/MapCompactWithConditionalBlock: # new in 1.30
|
||||
Enabled: true
|
||||
Style/MapIntoArray: # new in 1.63
|
||||
Enabled: true
|
||||
Style/MapToHash: # new in 1.24
|
||||
Enabled: true
|
||||
Style/MapToSet: # new in 1.42
|
||||
Enabled: true
|
||||
Style/MinMaxComparison: # new in 1.42
|
||||
Enabled: true
|
||||
Style/MultilineInPatternThen: # new in 1.16
|
||||
Enabled: true
|
||||
Style/NegatedIfElseCondition: # new in 1.2
|
||||
Enabled: true
|
||||
Style/NestedFileDirname: # new in 1.26
|
||||
Enabled: true
|
||||
Style/NilLambda: # new in 1.3
|
||||
Enabled: true
|
||||
Style/NumberedParameters: # new in 1.22
|
||||
Enabled: true
|
||||
Style/NumberedParametersLimit: # new in 1.22
|
||||
Enabled: true
|
||||
Style/ObjectThen: # new in 1.28
|
||||
Enabled: true
|
||||
Style/OpenStructUse: # new in 1.23
|
||||
Enabled: true
|
||||
Style/OperatorMethodCall: # new in 1.37
|
||||
Enabled: true
|
||||
Style/QuotedSymbols: # new in 1.16
|
||||
Enabled: true
|
||||
Style/RedundantArgument: # new in 1.4
|
||||
Enabled: true
|
||||
Style/RedundantArrayConstructor: # new in 1.52
|
||||
Enabled: true
|
||||
Style/RedundantConstantBase: # new in 1.40
|
||||
Enabled: true
|
||||
Style/RedundantCurrentDirectoryInPath: # new in 1.53
|
||||
Enabled: true
|
||||
Style/RedundantDoubleSplatHashBraces: # new in 1.41
|
||||
Enabled: true
|
||||
Style/RedundantEach: # new in 1.38
|
||||
Enabled: true
|
||||
Style/RedundantFilterChain: # new in 1.52
|
||||
Enabled: true
|
||||
Style/RedundantFormat: # new in 1.72
|
||||
Enabled: true
|
||||
Style/RedundantHeredocDelimiterQuotes: # new in 1.45
|
||||
Enabled: true
|
||||
Style/RedundantInitialize: # new in 1.27
|
||||
Enabled: true
|
||||
Style/RedundantInterpolationUnfreeze: # new in 1.66
|
||||
Enabled: true
|
||||
Style/RedundantLineContinuation: # new in 1.49
|
||||
Enabled: true
|
||||
Style/RedundantRegexpArgument: # new in 1.53
|
||||
Enabled: true
|
||||
Style/RedundantRegexpConstructor: # new in 1.52
|
||||
Enabled: true
|
||||
Style/RedundantSelfAssignmentBranch: # new in 1.19
|
||||
Enabled: true
|
||||
Style/RedundantStringEscape: # new in 1.37
|
||||
Enabled: true
|
||||
Style/ReturnNilInPredicateMethodDefinition: # new in 1.53
|
||||
Enabled: true
|
||||
Style/SafeNavigationChainLength: # new in 1.68
|
||||
Enabled: true
|
||||
Style/SelectByRegexp: # new in 1.22
|
||||
Enabled: true
|
||||
Style/SendWithLiteralMethodName: # new in 1.64
|
||||
Enabled: true
|
||||
Style/SingleLineDoEndBlock: # new in 1.57
|
||||
Enabled: true
|
||||
Style/StringChars: # new in 1.12
|
||||
Enabled: true
|
||||
Style/SuperArguments: # new in 1.64
|
||||
Enabled: true
|
||||
Style/SuperWithArgsParentheses: # new in 1.58
|
||||
Enabled: true
|
||||
Style/SwapValues: # new in 1.1
|
||||
Enabled: true
|
||||
Style/YAMLFileRead: # new in 1.53
|
||||
Enabled: true
|
||||
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@@ -0,0 +1 @@
|
||||
3.4.1
|
||||
49
.vscode/settings.json
vendored
Normal file
49
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"explorer.excludeGitIgnore": false,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"[ignore]": {
|
||||
"editor.defaultFormatter": "foxundermoon.shell-format"
|
||||
},
|
||||
"[shellscript]": {
|
||||
"editor.defaultFormatter": "mkhl.shfmt"
|
||||
},
|
||||
"[xml]": {
|
||||
"editor.defaultFormatter": "trunk.io"
|
||||
},
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "trunk.io"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "trunk.io"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
},
|
||||
"[yaml]": {
|
||||
"editor.defaultFormatter": "kennylong.kubernetes-yaml-formatter"
|
||||
},
|
||||
"[dockercompose]": {
|
||||
"editor.defaultFormatter": "ms-azuretools.vscode-containers"
|
||||
},
|
||||
"[svelte]": {
|
||||
"editor.defaultFormatter": "svelte.svelte-vscode"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "vscode.css-language-features"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "vscode.json-language-features"
|
||||
},
|
||||
"[rust]": {
|
||||
"editor.defaultFormatter": "rust-lang.rust-analyzer"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "vscode.json-language-features"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "Vue.volar"
|
||||
}
|
||||
}
|
||||
130
.zshrc
Normal file
130
.zshrc
Normal file
@@ -0,0 +1,130 @@
|
||||
if [ -f "$HOME/.init/init.sh" ] ; then
|
||||
. "$HOME/.init/init.sh"
|
||||
fi
|
||||
# Enable Powerlevel10k instant prompt. Should stay close to the top of ~/.zshrc.
|
||||
# Initialization code that may require console input (password prompts, [y/n]
|
||||
# confirmations, etc.) must go above this block; everything else may go below.
|
||||
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
|
||||
source "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh"
|
||||
fi
|
||||
|
||||
# If you come from bash you might have to change your $PATH.
|
||||
# export PATH=$HOME/bin:$HOME/.local/bin:/usr/local/bin:$PATH
|
||||
|
||||
# Path to your Oh My Zsh installation.
|
||||
export ZSH="$HOME/.oh-my-zsh"
|
||||
|
||||
# Set name of the theme to load --- if set to "random", it will
|
||||
# load a random theme each time Oh My Zsh is loaded, in which case,
|
||||
# to know which specific one was loaded, run: echo $RANDOM_THEME
|
||||
# See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes
|
||||
ZSH_THEME="powerlevel10k/powerlevel10k"
|
||||
|
||||
# Set list of themes to pick from when loading at random
|
||||
# Setting this variable when ZSH_THEME=random will cause zsh to load
|
||||
# a theme from this variable instead of looking in $ZSH/themes/
|
||||
# If set to an empty array, this variable will have no effect.
|
||||
# ZSH_THEME_RANDOM_CANDIDATES=( "robbyrussell" "agnoster" )
|
||||
|
||||
# Uncomment the following line to use case-sensitive completion.
|
||||
# CASE_SENSITIVE="true"
|
||||
|
||||
# Uncomment the following line to use hyphen-insensitive completion.
|
||||
# Case-sensitive completion must be off. _ and - will be interchangeable.
|
||||
# HYPHEN_INSENSITIVE="true"
|
||||
|
||||
# Uncomment one of the following lines to change the auto-update behavior
|
||||
# zstyle ':omz:update' mode disabled # disable automatic updates
|
||||
# zstyle ':omz:update' mode auto # update automatically without asking
|
||||
# zstyle ':omz:update' mode reminder # just remind me to update when it's time
|
||||
|
||||
# Uncomment the following line to change how often to auto-update (in days).
|
||||
# zstyle ':omz:update' frequency 13
|
||||
|
||||
# Uncomment the following line if pasting URLs and other text is messed up.
|
||||
# DISABLE_MAGIC_FUNCTIONS="true"
|
||||
|
||||
# Uncomment the following line to disable colors in ls.
|
||||
# DISABLE_LS_COLORS="true"
|
||||
|
||||
# Uncomment the following line to disable auto-setting terminal title.
|
||||
# DISABLE_AUTO_TITLE="true"
|
||||
|
||||
# Uncomment the following line to enable command auto-correction.
|
||||
# ENABLE_CORRECTION="true"
|
||||
|
||||
# Uncomment the following line to display red dots whilst waiting for completion.
|
||||
# You can also set it to another string to have that shown instead of the default red dots.
|
||||
# e.g. COMPLETION_WAITING_DOTS="%F{yellow}waiting...%f"
|
||||
# Caution: this setting can cause issues with multiline prompts in zsh < 5.7.1 (see #5765)
|
||||
# COMPLETION_WAITING_DOTS="true"
|
||||
|
||||
# Uncomment the following line if you want to disable marking untracked files
|
||||
# under VCS as dirty. This makes repository status check for large repositories
|
||||
# much, much faster.
|
||||
# DISABLE_UNTRACKED_FILES_DIRTY="true"
|
||||
|
||||
# Uncomment the following line if you want to change the command execution time
|
||||
# stamp shown in the history command output.
|
||||
# You can set one of the optional three formats:
|
||||
# "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd"
|
||||
# or set a custom format using the strftime function format specifications,
|
||||
# see 'man strftime' for details.
|
||||
# HIST_STAMPS="mm/dd/yyyy"
|
||||
|
||||
# Would you like to use another custom folder than $ZSH/custom?
|
||||
# ZSH_CUSTOM=/path/to/new-custom-folder
|
||||
|
||||
# Which plugins would you like to load?
|
||||
# Standard plugins can be found in $ZSH/plugins/
|
||||
# Custom plugins may be added to $ZSH_CUSTOM/plugins/
|
||||
# Example format: plugins=(rails git textmate ruby lighthouse)
|
||||
# Add wisely, as too many plugins slow down shell startup.
|
||||
plugins=(git pm2 gh sudo ssh ruby rust python node github rsync nvm rbenv pyenv docker docker-compose qrcode zsh-autosuggestions zsh-syntax-highlighting zsh-interactive-cd zsh-navigation-tools)
|
||||
|
||||
source $ZSH/oh-my-zsh.sh
|
||||
|
||||
# User configuration
|
||||
|
||||
# export MANPATH="/usr/local/man:$MANPATH"
|
||||
|
||||
# You may need to manually set your language environment
|
||||
# export LANG=en_US.UTF-8
|
||||
|
||||
# Preferred editor for local and remote sessions
|
||||
# if [[ -n $SSH_CONNECTION ]]; then
|
||||
# export EDITOR='vim'
|
||||
# else
|
||||
# export EDITOR='nvim'
|
||||
# fi
|
||||
|
||||
# Compilation flags
|
||||
# export ARCHFLAGS="-arch $(uname -m)"
|
||||
|
||||
# Set personal aliases, overriding those provided by Oh My Zsh libs,
|
||||
# plugins, and themes. Aliases can be placed here, though Oh My Zsh
|
||||
# users are encouraged to define aliases within a top-level file in
|
||||
# the $ZSH_CUSTOM folder, with .zsh extension. Examples:
|
||||
# - $ZSH_CUSTOM/aliases.zsh
|
||||
# - $ZSH_CUSTOM/macos.zsh
|
||||
# For a full list of active aliases, run `alias`.
|
||||
#
|
||||
# Example aliases
|
||||
# alias zshconfig="mate ~/.zshrc"
|
||||
# alias ohmyzsh="mate ~/.oh-my-zsh"
|
||||
|
||||
# To customize prompt, run `p10k configure` or edit ~/.p10k.zsh.
|
||||
[[ ! -f ~/.p10k.zsh ]] || source ~/.p10k.zsh
|
||||
|
||||
|
||||
# _home_pull
|
||||
|
||||
cd "$HOME/$(cat $HOME/.last_pwd)" &>/dev/null
|
||||
|
||||
# pnpm
|
||||
export PNPM_HOME="/home/valknar/.local/share/pnpm"
|
||||
case ":$PATH:" in
|
||||
*":$PNPM_HOME:"*) ;;
|
||||
*) export PATH="$PNPM_HOME:$PATH" ;;
|
||||
esac
|
||||
# pnpm end
|
||||
137
CLAUDE.md
Normal file
137
CLAUDE.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Overview
|
||||
|
||||
This is a personal home directory repository managed as a git repository with selective tracking via `.gitignore`. The repository tracks dotfiles and configuration for a Debian development environment supporting Node.js, Python, Ruby, Rust, and Go development.
|
||||
|
||||
## Key Architecture
|
||||
|
||||
### Initialization System
|
||||
Shell initialization is managed through `.init/init.sh`, which sources modular configuration:
|
||||
- `.init/path.sh` - PATH environment setup for all language toolchains
|
||||
- `.init/alias.sh` - Custom shell aliases
|
||||
- `.init/functions.sh` - Custom shell functions for deployment and media processing
|
||||
- `.init/export.sh`, `.init/source.sh`, `.init/eval.sh` - Additional environment setup
|
||||
|
||||
### Arty Configuration
|
||||
`arty.yml` defines the repository structure using Arty (artifact/repository manager):
|
||||
- **references**: Git subrepositories to clone into specific paths
|
||||
- **envs**: Environment profiles (dev/prod) for selective repository management
|
||||
- Manages both development projects and language version managers (nvm, rbenv, pyenv, gvm)
|
||||
|
||||
### Ansible Provisioning
|
||||
`playbook.yml` is an Ansible playbook for system setup:
|
||||
- Installs and configures language runtimes (Node, Python, Ruby, Rust, Go)
|
||||
- Sets up Docker, PostgreSQL 18, and development tools
|
||||
- Configures Zsh with Oh-My-Zsh and Powerlevel10k theme
|
||||
- Manages system packages via apt
|
||||
|
||||
### Git Selective Tracking
|
||||
The `.gitignore` uses an inverted pattern (ignore everything, then selectively allow):
|
||||
- Tracks only specific dotfiles and configuration files
|
||||
- Allows `.github/`, `.init/`, `.vscode/` directories
|
||||
- Excludes logs, databases, and temporary files
|
||||
|
||||
## Development Environment
|
||||
|
||||
### Language Version Management
|
||||
- **Node.js**: Managed by nvm, version specified in `.nvmrc`
|
||||
- **Ruby**: Managed by rbenv, version in `.ruby-version`
|
||||
- **Python**: Managed by pyenv, version in `.python-version`
|
||||
- **Rust**: Via rustup (`.cargo/`, `.rustup/`)
|
||||
- **Go**: Via gvm (`.gvm/`)
|
||||
|
||||
### Shell Environment
|
||||
- **Shell**: Zsh with Oh-My-Zsh framework
|
||||
- **Theme**: Powerlevel10k (`.p10k.zsh`)
|
||||
- **Plugins**: git, pm2, gh, docker, language-specific plugins, zsh-autosuggestions, zsh-syntax-highlighting
|
||||
|
||||
## Common Commands
|
||||
|
||||
### Environment Setup
|
||||
```bash
|
||||
# Reinitialize shell environment
|
||||
ri # alias for: source ~/.init/init.sh
|
||||
|
||||
# Bootstrap system (run as user, prompts for sudo)
|
||||
sudo -u $USER ansible-playbook -K playbook.yml
|
||||
|
||||
# Run specific Ansible tags
|
||||
ansible-playbook --tags node,python,ruby -K playbook.yml
|
||||
```
|
||||
|
||||
### Arty Repository Management
|
||||
```bash
|
||||
# Debug Arty configuration
|
||||
pnpm arty debug
|
||||
|
||||
# Clone/update repositories based on environment
|
||||
pnpm arty sync --env dev
|
||||
```
|
||||
|
||||
### Git Workflow
|
||||
```bash
|
||||
# Stage all changes and check if clean
|
||||
g0 # alias for: git add . && git diff --quiet && git diff --cached --quiet
|
||||
|
||||
# Reset to single commit
|
||||
g1 # alias for: git reset $(git commit-tree "HEAD^{tree}" -m "A new start")
|
||||
|
||||
# Get last commit message
|
||||
g2 # alias for: git log --format=%B -n 1 HEAD | head -n 1
|
||||
```
|
||||
|
||||
### Code Quality
|
||||
```bash
|
||||
# Python pre-commit hooks (configured in .pre-commit-config.yaml)
|
||||
pre-commit run --all-files
|
||||
|
||||
# Ruby style checking
|
||||
rubocop
|
||||
|
||||
# Node.js linting
|
||||
pnpm eslint
|
||||
```
|
||||
|
||||
### Utility Functions
|
||||
Available shell functions from `.init/functions.sh`:
|
||||
- `batch_file_sequence <prefix> <extension>` - Rename files with sequence numbers
|
||||
- `batch_image_webp` - Convert images to WebP format
|
||||
- `batch_video_x264` - Convert videos to x264 codec
|
||||
- `rs` - Rsync with sudo on remote (alias for complex rsync command)
|
||||
- `ss` - Serve current directory on port 8000
|
||||
- `yt <url>` - Download YouTube video as MP3
|
||||
|
||||
## Projects Structure
|
||||
|
||||
The `Projects/` directory contains development projects:
|
||||
- `butter-sh/` - Butter shell projects
|
||||
- `docker-compose/` - Docker compose configurations
|
||||
- `pivoine.art/` - Jekyll-based art portfolio site
|
||||
- `docs.pivoine.art/` - Documentation site
|
||||
- `sexy.pivoine.art/` - Includes Rust package (`packages/buttplug/`)
|
||||
- `node.js/` - Node.js applications (awesome, awesome-app, email-pour-vous, webshot)
|
||||
|
||||
## Package Management
|
||||
|
||||
### Node.js
|
||||
- **Package manager**: pnpm (enabled via corepack)
|
||||
- **Global packages**: Installed to `~/node_modules/`
|
||||
- **PM2**: Configured via `ecosystem.config.js` for GitHub Copilot language server
|
||||
|
||||
### Python
|
||||
- **Installer**: pip
|
||||
- **Dependencies**: Listed in `requirements.txt` (currently: pre-commit)
|
||||
|
||||
### Ruby
|
||||
- **Bundler**: Gemfile specifies Jekyll 4.3 and rubocop
|
||||
|
||||
## Important Notes
|
||||
|
||||
- This repository uses selective git tracking - most files are ignored by default
|
||||
- Shell must source `.init/init.sh` for full environment setup (automatically done in `.zshrc`)
|
||||
- Language runtimes are version-managed and installed via Ansible
|
||||
- Docker requires user to be in `docker` group (managed by Ansible)
|
||||
- The `.last_pwd` file tracks the last working directory for shell navigation
|
||||
7
Gemfile
Normal file
7
Gemfile
Normal file
@@ -0,0 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
source 'https://rubygems.org'
|
||||
|
||||
|
||||
gem 'jekyll', '~> 4.3'
|
||||
gem 'rubocop'
|
||||
478
README.md
Executable file
478
README.md
Executable file
@@ -0,0 +1,478 @@
|
||||
<div align="center">
|
||||
|
||||
<pre>
|
||||
_ _____ __ __ __ _ _____ ____ _ _____
|
||||
| | / / | / / / //_// | / / | / __ ( ) ___/
|
||||
| | / / /| | / / / ,< / |/ / /| | / /_/ //\__ \
|
||||
| |/ / ___ |/ /___/ /| |/ /| / ___ |/ _, _/ ___/ /
|
||||
|___/_/ |_/_____/_/ |_/_/ |_/_/ |_/_/ |_| /____/
|
||||
|
||||
__________ ____ ____________
|
||||
/ ____/ __ \/ __ \/ ____/ ____/
|
||||
/ /_ / / / / /_/ / / __/ __/
|
||||
/ __/ / /_/ / _, _/ /_/ / /___
|
||||
/_/ \____/_/ |_|\____/_____/
|
||||
</pre>
|
||||
|
||||
# ⚡🔥 WHERE CODE MEETS CHAOS 🔥⚡
|
||||
|
||||
[](https://www.debian.org/)
|
||||
[](https://www.slayer.net/)
|
||||
[](/)
|
||||
[](LICENSE)
|
||||
|
||||
**My Debian home directory - forged in the fires of chaos, tempered with configuration files,**
|
||||
**and wielded with the fury of a thousand riffs.**
|
||||
|
||||
*This is where dotfiles headbang and shell scripts scream.*
|
||||
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## ⚡ THE ARSENAL ⚡
|
||||
|
||||
### 🎸 **WEAPONS OF MASS DEVELOPMENT**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ ⚔️ NODE.JS │ Managed by nvm │
|
||||
│ ⚔️ PYTHON │ Managed by pyenv │
|
||||
│ ⚔️ RUBY │ Managed by rbenv │
|
||||
│ ⚔️ RUST │ Managed by rustup │
|
||||
│ ⚔️ GO │ Managed by gvm │
|
||||
│ ⚔️ DOCKER │ Containerized destruction │
|
||||
│ ⚔️ POSTGRES │ Version 18 database engine │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔴 INITIALIZATION RITUAL 🔴
|
||||
|
||||
### **The `.init/` System - Your Shell's Dark Ceremony**
|
||||
|
||||
The `.init/` directory is the beating heart of this environment. When your shell awakens, it performs a sacred ritual through `~/.init/init.sh`, summoning power from these ancient scripts:
|
||||
|
||||
#### 🗡️ **THE SEVEN STAGES OF POWER**
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────────────────┐
|
||||
│ 1. PATH.SH │ Forges the PATH to all binaries │
|
||||
│ 2. EXPORT.SH │ Exports environment variables │
|
||||
│ 3. ALIAS.SH │ Summons command shortcuts │
|
||||
│ 4. SOURCE.SH │ Sources external power (nvm, cargo, etc) │
|
||||
│ 5. FUNCTIONS.SH │ Unleashes custom shell functions │
|
||||
│ 6. EVAL.SH │ Evaluates version managers (rbenv, etc) │
|
||||
│ 7. START.SH │ Executes startup commands (ssh-add) │
|
||||
└──────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### 📜 **DETAILED BREAKDOWN OF EACH SCRIPT**
|
||||
|
||||
##### **1️⃣ `path.sh` - The Path of Destruction**
|
||||
Adds all binary directories to your `$PATH`:
|
||||
- `~/bin` - Your personal executables
|
||||
- `~/.local/bin` - Local user binaries
|
||||
- `~/.rbenv/bin` - Ruby version manager
|
||||
- `~/.pyenv/bin` - Python version manager
|
||||
- `~/.cargo/bin` - Rust binaries
|
||||
- `~/go/bin` - Go binaries
|
||||
- `~/node_modules/.bin` - Node.js binaries
|
||||
- `~/.init/bin` - Custom init scripts
|
||||
- `~/Projects/kompose` - Kompose tooling
|
||||
|
||||
##### **2️⃣ `export.sh` - Environmental Warfare**
|
||||
```bash
|
||||
export NVM_DIR="$HOME/.nvm" # Node Version Manager home
|
||||
export REPOS_DIR="$HOME/repos" # Repository directory
|
||||
export CHORE_CHORE="chore: chore" # Default commit message
|
||||
```
|
||||
|
||||
##### **3️⃣ `alias.sh` - Command Line Sorcery**
|
||||
```bash
|
||||
ri # Reload init: source ~/.init/init.sh
|
||||
g0 # Git stage all and check if clean
|
||||
g1 # Git reset to single commit (nuclear option)
|
||||
g2 # Get last commit message
|
||||
rs # Rsync with sudo on remote
|
||||
ss # Serve static files on port 8000
|
||||
yt # Download YouTube as MP3
|
||||
```
|
||||
|
||||
##### **4️⃣ `source.sh` - External Power Summoning**
|
||||
Sources critical external scripts:
|
||||
- **NVM** - Node Version Manager (`$NVM_DIR/nvm.sh`)
|
||||
- **RVM** - Ruby Version Manager (commented out)
|
||||
- **Cargo** - Rust environment (`~/.cargo/env`)
|
||||
- **Bash completion** for NVM
|
||||
|
||||
##### **5️⃣ `functions.sh` - The Grimoire of Bash**
|
||||
Custom functions for deployment and media manipulation:
|
||||
|
||||
**Git Warfare:**
|
||||
- `_home_push` - Commit and push changes
|
||||
- `_home_pull` - Stash, pull, and pop changes
|
||||
|
||||
**Deployment Spells:**
|
||||
- `_site_deploy_jekyll <site>` - Build & deploy Jekyll site
|
||||
- `_site_deploy_nuxt <site>` - Build & deploy Nuxt site
|
||||
- `_site_deploy_static <site>` - Deploy static files
|
||||
|
||||
**Media Alchemy:**
|
||||
- `batch_file_sequence <prefix> <ext>` - Rename files with numbers
|
||||
- `batch_image_webp` - Convert all images to WebP
|
||||
- `batch_video_x264` - Convert videos to x264 codec
|
||||
- `_image_optimize <name>` - Full image optimization pipeline
|
||||
- `_video_optimize <file>` - Optimize video with ffmpeg
|
||||
|
||||
##### **6️⃣ `eval.sh` - Version Manager Invocation**
|
||||
Initializes version managers through `eval`:
|
||||
- **oh-my-posh** - Shell prompt theme engine
|
||||
- **rbenv** - Ruby version manager
|
||||
- **pyenv** - Python version manager
|
||||
|
||||
##### **7️⃣ `start.sh` - The Final Awakening**
|
||||
Executes startup commands:
|
||||
- `ssh-add` - Adds SSH keys to the agent silently
|
||||
|
||||
#### 🗂️ **Additional Directories**
|
||||
|
||||
- **`.init/bin/`** - Custom executable scripts (e.g., `mime_mp4_gif.sh`)
|
||||
- **`.init/hooks/`** - Reserved for shell hooks (currently empty)
|
||||
|
||||
---
|
||||
|
||||
## 🩸 QUICK START RITUAL 🩸
|
||||
|
||||
### **Summoning the Environment**
|
||||
|
||||
```bash
|
||||
# 1. Clone this unholy repository
|
||||
git init && git remote add origin git@github.com:valknarogg/home.git
|
||||
git fetch && git reset --hard origin/main
|
||||
git branch --set-upstream-to=origin/main main
|
||||
|
||||
# 2. Install Ansible (if not already installed)
|
||||
sudo apt install git ansible
|
||||
|
||||
# 3. Configure git
|
||||
git config --global init.defaultBranch main
|
||||
git config --global --add safe.directory /home/$USER
|
||||
|
||||
# 4. Unleash the Ansible playbook
|
||||
sudo -u $USER ansible-playbook -K playbook.yml
|
||||
```
|
||||
|
||||
### **Selective Provisioning**
|
||||
|
||||
Run specific parts of the setup using tags:
|
||||
|
||||
```bash
|
||||
# Install only Node.js environment
|
||||
ansible-playbook --tags node -K playbook.yml
|
||||
|
||||
# Install Python + Ruby
|
||||
ansible-playbook --tags python,ruby -K playbook.yml
|
||||
|
||||
# Install everything
|
||||
ansible-playbook -K playbook.yml
|
||||
```
|
||||
|
||||
#### 🏷️ **Available Tags:**
|
||||
`base` | `node` | `python` | `ruby` | `rust` | `zsh` | `postgres` | `docker` | `fonts` | `flatpak` | `github` | `oh-my-posh`
|
||||
|
||||
---
|
||||
|
||||
## 🎯 ARTY - REPOSITORY ORCHESTRATION 🎯
|
||||
|
||||
### **What is Arty?**
|
||||
|
||||
**Arty.sh** is a bash-based dependency and repository manager that orchestrates git subrepositories like a conductor of chaos. It's part of the [butter.sh](https://github.com/butter-sh/butter-sh.github.io) ecosystem - a suite of bash development tools.
|
||||
|
||||
### **Installing Arty**
|
||||
|
||||
Arty is already installed globally at `/usr/local/bin/arty`. If you need to install/update it:
|
||||
|
||||
```bash
|
||||
# Clone butter.sh ecosystem
|
||||
git clone https://github.com/butter-sh/butter-sh.github.io.git ~/Projects/butter-sh
|
||||
|
||||
# Install arty globally (requires sudo)
|
||||
cd ~/Projects/butter-sh/projects/arty.sh
|
||||
sudo ./arty.sh install
|
||||
```
|
||||
|
||||
### **The `arty.yml` Configuration**
|
||||
|
||||
The root `~/arty.yml` defines your entire repository ecosystem:
|
||||
|
||||
```yaml
|
||||
name: "Valknar's home"
|
||||
version: "1.0.0"
|
||||
|
||||
envs:
|
||||
dev: # Development environment
|
||||
prod: # Production environment
|
||||
|
||||
references:
|
||||
# Project repositories
|
||||
- url: git@github.com:valknarogg/pivoine.art.git
|
||||
into: Projects/pivoine.art
|
||||
env: dev
|
||||
|
||||
# Media repositories
|
||||
- url: git@github.com:valknarogg/home-pictures.git
|
||||
into: Bilder
|
||||
env: dev
|
||||
|
||||
# Version managers
|
||||
- url: https://github.com/nvm-sh/nvm.git
|
||||
into: .nvm
|
||||
|
||||
# ... and many more
|
||||
|
||||
scripts:
|
||||
debug: echo "$ARTY_BIN_DIR" && echo "$ARTY_LIBS_DIR"
|
||||
```
|
||||
|
||||
### **Using Arty**
|
||||
|
||||
```bash
|
||||
# Sync all dev environment repositories
|
||||
arty sync --env dev
|
||||
|
||||
# Sync production repositories only
|
||||
arty sync --env prod
|
||||
|
||||
# Install dependencies from arty.yml
|
||||
arty install
|
||||
|
||||
# Run custom scripts defined in arty.yml
|
||||
arty debug
|
||||
|
||||
# Show dependency tree
|
||||
arty deps
|
||||
|
||||
# Update a specific reference
|
||||
arty update pivoine.art
|
||||
```
|
||||
|
||||
### **What Arty Manages:**
|
||||
- ✅ Project repositories (pivoine.art, sexy.pivoine.art, etc.)
|
||||
- ✅ Media repositories (Pictures, Videos, Music)
|
||||
- ✅ Docker compose configurations
|
||||
- ✅ Version managers (nvm, rbenv, pyenv, gvm)
|
||||
- ✅ Oh-My-Zsh and plugins
|
||||
- ✅ Shell scripts and binaries
|
||||
|
||||
### **Environment-Based Management**
|
||||
|
||||
References can be tagged with `env: dev` or `env: prod` to control which repositories are synced in different environments. This allows you to:
|
||||
- Keep heavy media files out of production servers
|
||||
- Separate development projects from system utilities
|
||||
- Maintain clean, minimal deployments
|
||||
|
||||
---
|
||||
|
||||
## 🔥 COMMAND LINE BRUTALITY 🔥
|
||||
|
||||
### **Git Operations**
|
||||
|
||||
```bash
|
||||
g0 # Stage all changes and verify clean state
|
||||
g1 # Nuclear reset to single commit
|
||||
g2 # Show last commit message
|
||||
git add -A && git commit -m "$(g2)" # Reuse last commit message
|
||||
```
|
||||
|
||||
### **Media Processing**
|
||||
|
||||
```bash
|
||||
# Convert all images in directory to WebP
|
||||
batch_image_webp
|
||||
|
||||
# Rename files with sequence numbers
|
||||
batch_file_sequence artwork webp
|
||||
|
||||
# Optimize video
|
||||
_video_optimize input.mov
|
||||
|
||||
# Download YouTube video as MP3
|
||||
yt "https://youtube.com/watch?v=..."
|
||||
```
|
||||
|
||||
### **Development Servers**
|
||||
|
||||
```bash
|
||||
# Serve current directory on port 8000
|
||||
ss
|
||||
|
||||
# Run Jekyll site with livereload
|
||||
cd ~/Projects/pivoine.art && bundle exec jekyll serve --livereload
|
||||
|
||||
# Run Node.js dev server
|
||||
cd ~/Projects/node.js/awesome && pnpm dev
|
||||
```
|
||||
|
||||
### **Rsync Power**
|
||||
|
||||
```bash
|
||||
# Sync to remote with sudo
|
||||
rs /local/path/ user@host:/remote/path/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📁 PROJECT STRUCTURE 📁
|
||||
|
||||
```
|
||||
~/Projects/
|
||||
├── butter-sh/ # Butter.sh ecosystem (arty, judge, myst, etc.)
|
||||
├── docker-compose/ # Docker orchestration configs
|
||||
├── pivoine.art/ # Jekyll art portfolio (main site)
|
||||
├── docs.pivoine.art/ # Documentation site
|
||||
├── sexy.pivoine.art/ # Rust + web project (includes buttplug package)
|
||||
└── node.js/
|
||||
├── awesome/ # GitHub Awesome lists browser
|
||||
├── awesome-app/ # Awesome list application
|
||||
├── email-pour-vous/ # Email templating project
|
||||
└── webshot/ # Website screenshot tool
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ DOTFILE HIGHLIGHTS 🛠️
|
||||
|
||||
### **Shell Configuration**
|
||||
- **`.zshrc`** - Oh-My-Zsh with Powerlevel10k theme
|
||||
- **`.p10k.zsh`** - Powerlevel10k configuration
|
||||
- **`.bashrc`** - Bash configuration (fallback)
|
||||
|
||||
### **Version Files**
|
||||
- **`.nvmrc`** - Node.js version
|
||||
- **`.ruby-version`** - Ruby version
|
||||
- **`.python-version`** - Python version
|
||||
|
||||
### **Code Quality**
|
||||
- **`.pre-commit-config.yaml`** - Pre-commit hooks (Python)
|
||||
- **`.rubocop.yml`** - Ruby style enforcement
|
||||
- **`eslint.config.mts`** - JavaScript/TypeScript linting
|
||||
- **`.prettierrc`** - Code formatting rules
|
||||
- **`biome.json`** - Fast linter/formatter
|
||||
|
||||
### **Package Management**
|
||||
- **`requirements.txt`** - Python packages (pip)
|
||||
- **`Gemfile`** - Ruby gems (bundler)
|
||||
|
||||
### **Git Configuration**
|
||||
- **`.gitignore`** - INVERTED PATTERN (ignore all, allow specific files)
|
||||
- **`.gitconfig`** - Git user configuration
|
||||
|
||||
### **Orchestration**
|
||||
- **`arty.yml`** - Repository and dependency management
|
||||
- **`playbook.yml`** - Ansible system provisioning
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ GIT SELECTIVE TRACKING ⚙️
|
||||
|
||||
This repository uses an **inverted `.gitignore`** pattern:
|
||||
|
||||
```gitignore
|
||||
# Ignore everything
|
||||
*
|
||||
|
||||
# Allow specific files
|
||||
!CLAUDE.md
|
||||
!README.md
|
||||
!.gitignore
|
||||
!.init/**
|
||||
!arty.yml
|
||||
!playbook.yml
|
||||
...
|
||||
```
|
||||
|
||||
**Why?** To track only essential dotfiles and configurations while ignoring cache, logs, and user data. Your home directory becomes a git repository without the chaos.
|
||||
|
||||
---
|
||||
|
||||
## 🎸 SHELL PLUGIN POWER 🎸
|
||||
|
||||
**Oh-My-Zsh Plugins Loaded:**
|
||||
```
|
||||
git pm2 gh sudo ssh ruby rust python node github
|
||||
rsync nvm rbenv pyenv docker docker-compose qrcode
|
||||
zsh-autosuggestions zsh-syntax-highlighting
|
||||
zsh-interactive-cd zsh-navigation-tools
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔗 USEFUL RESOURCES 🔗
|
||||
|
||||
### System & Shell
|
||||
- [Ansible Documentation](https://docs.ansible.com/)
|
||||
- [Oh-My-Zsh](https://ohmyz.sh/)
|
||||
- [Powerlevel10k](https://github.com/romkatv/powerlevel10k)
|
||||
|
||||
### Language Managers
|
||||
- [nvm](https://github.com/nvm-sh/nvm) - Node Version Manager
|
||||
- [rbenv](https://github.com/rbenv/rbenv) - Ruby Version Manager
|
||||
- [pyenv](https://github.com/pyenv/pyenv) - Python Version Manager
|
||||
- [gvm](https://github.com/moovweb/gvm) - Go Version Manager
|
||||
- [rustup](https://rustup.rs/) - Rust Toolchain Manager
|
||||
|
||||
### Orchestration
|
||||
- [Arty.sh Documentation](https://github.com/butter-sh/butter-sh.github.io)
|
||||
- [Butter.sh Ecosystem](https://butter.sh)
|
||||
|
||||
---
|
||||
|
||||
## 🖤 LICENSE 🖤
|
||||
|
||||
MIT License - Do whatever the hell you want with it.
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
|
||||
<pre>
|
||||
═════════════════════════════════════════════════════════════════
|
||||
|
||||
__________ ____ ______ _________
|
||||
/ ____/ __ \/ __ \/ ____/ / _/ ___/
|
||||
/ / / / / / / / / __/ / / \__ \
|
||||
/ /___/ /_/ / /_/ / /___ _/ / ___/ /
|
||||
\____/\____/_____/_____/ /___//____/
|
||||
|
||||
______________ _______ ____ ____ ___ ______ __
|
||||
/_ __/ ____/ |/ / __ \/ __ \/ __ \/ | / __ \ \/ /
|
||||
/ / / __/ / /|_/ / /_/ / / / / /_/ / /| | / /_/ /\ /
|
||||
/ / / /___/ / / / ____/ /_/ / _, _/ ___ |/ _, _/ / /
|
||||
/_/ /_____/_/ /_/_/ \____/_/ |_/_/ |_/_/ |_| /_/
|
||||
|
||||
__ __________________ __ _________
|
||||
/ |/ / ____/_ __/ | / / / _/ ___/
|
||||
/ /|_/ / __/ / / / /| | / / / / \__ \
|
||||
/ / / / /___ / / / ___ |/ /___ _/ / ___/ /
|
||||
/_/ /_/_____/ /_/ /_/ |_/_____/ /___//____/
|
||||
|
||||
______________________ _ _____ __
|
||||
/ ____/_ __/ ____/ __ \/ | / / | / /
|
||||
/ __/ / / / __/ / /_/ / |/ / /| | / /
|
||||
/ /___ / / / /___/ _, _/ /| / ___ |/ /___
|
||||
/_____/ /_/ /_____/_/ |_/_/ |_/_/ |_/_____/
|
||||
|
||||
═════════════════════════════════════════════════════════════════
|
||||
|
||||
🔥⚡ FORGED BY VALKNAR ⚡🔥
|
||||
valknar@pivoine.art
|
||||
Powered by Debian | Fueled by Metal
|
||||
|
||||
🤘 🤘 🤘
|
||||
</pre>
|
||||
|
||||
**[⚔️ BACK TO THE TOP ⚔️](#)**
|
||||
|
||||
</div>
|
||||
65
arty.yml
Normal file
65
arty.yml
Normal file
@@ -0,0 +1,65 @@
|
||||
name: "Valknar's home"
|
||||
version: "1.0.0"
|
||||
description: "Valknar's home repository"
|
||||
author: "valknar@pivoine.art"
|
||||
license: "MIT"
|
||||
|
||||
envs:
|
||||
dev:
|
||||
prod:
|
||||
|
||||
|
||||
references:
|
||||
- url: git@github.com:butter-sh/butter-sh.github.io.git
|
||||
into: Projects/butter-sh
|
||||
env: dev
|
||||
- url: git@github.com:valknarogg/home-pictures.git
|
||||
into: Bilder
|
||||
env: dev
|
||||
- url: git@github.com:valknarogg/home-videos.git
|
||||
into: Videos
|
||||
ref: main
|
||||
env: dev
|
||||
- url: git@github.com:valknarogg/home-music.git
|
||||
into: Musik
|
||||
ref: main
|
||||
env: dev
|
||||
- url: git@github.com:valknarogg/docker-compose.git
|
||||
into: Projects/docker-compose
|
||||
env:
|
||||
- dev
|
||||
- prod
|
||||
- url: git@github.com:valknarogg/pivoine.art.git
|
||||
into: Projects/pivoine.art
|
||||
ref: main
|
||||
env: dev
|
||||
- url: git@github.com:valknarxxx/sexy.pivoine.art.git
|
||||
into: Projects/sexy.pivoine.art
|
||||
env: dev
|
||||
- url: git@github.com:valknarness/awesome.git
|
||||
into: Projects/node.js/awesome
|
||||
env:
|
||||
- dev
|
||||
- prod
|
||||
- url: git@github.com:valknarness/awesome-app.git
|
||||
into: Projects/node.js/awesome-app
|
||||
env: dev
|
||||
- url: https://github.com/nvm-sh/nvm.git
|
||||
into: .nvm
|
||||
- url: https://github.com/moovweb/gvm.git
|
||||
into: .gvm
|
||||
- url: https://github.com/rbenv/rbenv.git
|
||||
into: .rbenv
|
||||
- url: https://github.com/pyenv/pyenv.git
|
||||
into: .pyenv
|
||||
- url: https://github.com/ohmyzsh/ohmyzsh.git
|
||||
into: .oh-my-zsh
|
||||
- url: https://github.com/romkatv/powerlevel10k.git
|
||||
into: .oh-my-zsh/custom/themes/powerlevel10k
|
||||
- url: https://github.com/zsh-users/zsh-autosuggestions.git
|
||||
into: .oh-my-zsh/custom/plugins/zsh-autosuggestions
|
||||
- url: https://github.com/zsh-users/zsh-syntax-highlighting.git
|
||||
into: .oh-my-zsh/custom/plugins/zsh-syntax-highlighting
|
||||
|
||||
scripts:
|
||||
debug: echo "$ARTY_BIN_DIR" && echo "$ARTY_LIBS_DIR" && echo "$ARTY_HOME"
|
||||
34
biome.json
Normal file
34
biome.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.2.4/schema.json",
|
||||
"vcs": {
|
||||
"enabled": true,
|
||||
"clientKind": "git",
|
||||
"useIgnoreFile": true
|
||||
},
|
||||
"files": {
|
||||
"ignoreUnknown": false
|
||||
},
|
||||
"formatter": {
|
||||
"enabled": true,
|
||||
"indentStyle": "tab"
|
||||
},
|
||||
"linter": {
|
||||
"enabled": true,
|
||||
"rules": {
|
||||
"recommended": true
|
||||
}
|
||||
},
|
||||
"javascript": {
|
||||
"formatter": {
|
||||
"quoteStyle": "double"
|
||||
}
|
||||
},
|
||||
"assist": {
|
||||
"enabled": true,
|
||||
"actions": {
|
||||
"source": {
|
||||
"organizeImports": "on"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
ecosystem.config.js
Normal file
9
ecosystem.config.js
Normal file
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
apps: [
|
||||
{
|
||||
name: "language-server",
|
||||
script:
|
||||
"./node_modules/@github/copilot-language-server/dist/language-server.js",
|
||||
},
|
||||
],
|
||||
};
|
||||
14
eslint.config.mts
Normal file
14
eslint.config.mts
Normal file
@@ -0,0 +1,14 @@
|
||||
import js from "@eslint/js";
|
||||
import globals from "globals";
|
||||
import tseslint from "typescript-eslint";
|
||||
import { defineConfig } from "eslint/config";
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
|
||||
plugins: { js },
|
||||
extends: ["js/recommended"],
|
||||
languageOptions: { globals: globals.browser },
|
||||
},
|
||||
tseslint.configs.recommended,
|
||||
]);
|
||||
288
playbook.yml
Normal file
288
playbook.yml
Normal file
@@ -0,0 +1,288 @@
|
||||
- hosts: localhost
|
||||
connection: local
|
||||
tasks:
|
||||
- name: Install base packages
|
||||
become: true
|
||||
|
||||
ansible.builtin.apt:
|
||||
pkg:
|
||||
- make
|
||||
- build-essential
|
||||
- git
|
||||
- curl
|
||||
- wget
|
||||
- rsync
|
||||
- zsh
|
||||
- imagemagick
|
||||
- ffmpeg
|
||||
- yt-dlp
|
||||
- fzf
|
||||
- icoutils
|
||||
- postgresql-common
|
||||
- unzip
|
||||
tags:
|
||||
- base
|
||||
- python
|
||||
- ruby
|
||||
- rust
|
||||
- zsh
|
||||
- postgres
|
||||
|
||||
- name: Prepare postgresql
|
||||
ansible.builtin.shell: /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags:
|
||||
- postgres
|
||||
|
||||
- name: Install postgres package
|
||||
become: true
|
||||
ansible.builtin.apt:
|
||||
pkg:
|
||||
- postgresql-18
|
||||
update_cache: true
|
||||
tags:
|
||||
- postgres
|
||||
|
||||
- name: Change root shell to zsh
|
||||
become: true
|
||||
ansible.builtin.user:
|
||||
name: root
|
||||
shell: /bin/zsh
|
||||
tags:
|
||||
- oh-my-posh
|
||||
|
||||
- name: Give permissions to /root/bin
|
||||
become: true
|
||||
ansible.builtin.file:
|
||||
path: /root/bin
|
||||
state: directory
|
||||
tags:
|
||||
- oh-my-posh
|
||||
|
||||
- name: Install oh-my-posh for root
|
||||
become: true
|
||||
ansible.builtin.shell: curl -s https://ohmyposh.dev/install.sh | bash -s -- -d /root/bin
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags:
|
||||
- oh-my-posh
|
||||
|
||||
- name: Ensure root shell loads user bin
|
||||
become: true
|
||||
ansible.builtin.lineinfile:
|
||||
path: /root/.zshrc
|
||||
line: |
|
||||
export PATH="$PATH:/root/bin"
|
||||
if [ "$TERM_PROGRAM" != "Apple_Terminal" ]; then
|
||||
eval "$(oh-my-posh init zsh)"
|
||||
fi
|
||||
create: yes
|
||||
tags:
|
||||
- oh-my-posh
|
||||
|
||||
- name: Install node
|
||||
ansible.builtin.shell: source ~/.init/init.sh && nvm install
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags:
|
||||
- node
|
||||
|
||||
- name: Enable node corepack
|
||||
ansible.builtin.shell: source ~/.init/init.sh && corepack enable
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags:
|
||||
- node
|
||||
|
||||
- name: Install node packages
|
||||
ansible.builtin.shell: source ~/.init/init.sh && cd ~ && pnpm install
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags:
|
||||
- node
|
||||
|
||||
- name: Install python required packages
|
||||
become: true
|
||||
ansible.builtin.apt:
|
||||
pkg:
|
||||
- libssl-dev
|
||||
- zlib1g-dev
|
||||
- libbz2-dev
|
||||
- libreadline-dev
|
||||
- libsqlite3-dev
|
||||
- llvm
|
||||
- libncurses5-dev
|
||||
tags:
|
||||
- python
|
||||
|
||||
- name: Install python
|
||||
ansible.builtin.shell: source ~/.init/init.sh && pyenv install --skip-existing
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags:
|
||||
- python
|
||||
|
||||
- name: Install python packages
|
||||
ansible.builtin.shell: source ~/.init/init.sh && pip install -r ~/requirements.txt
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags:
|
||||
- python
|
||||
|
||||
- name: Init python pre-commit
|
||||
ansible.builtin.shell: source ~/.init/init.sh && cd ~ && pre-commit install
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags:
|
||||
- python
|
||||
|
||||
- name: Install ruby required packages
|
||||
become: true
|
||||
ansible.builtin.apt:
|
||||
pkg:
|
||||
- libssl-dev
|
||||
- libffi-dev
|
||||
- libyaml-dev
|
||||
- zlib1g-dev
|
||||
tags:
|
||||
- ruby
|
||||
|
||||
- name: Install ruby
|
||||
ansible.builtin.shell: source ~/.init/init.sh && rbenv install --skip-existing
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags:
|
||||
- ruby
|
||||
|
||||
- name: Install ruby base packages
|
||||
ansible.builtin.shell: source ~/.init/init.sh && gem install {{ item }}
|
||||
args:
|
||||
executable: /bin/bash
|
||||
with_items:
|
||||
- bundler
|
||||
tags:
|
||||
- ruby
|
||||
|
||||
- name: Install ruby packages
|
||||
ansible.builtin.shell: source ~/.init/init.sh && cd ~ && bundle install
|
||||
args:
|
||||
executable: /bin/bash
|
||||
tags:
|
||||
- ruby
|
||||
|
||||
- name: Install rust required packages
|
||||
become: true
|
||||
ansible.builtin.apt:
|
||||
pkg:
|
||||
- libudev-dev
|
||||
- libusb-1.0-0-dev
|
||||
- libdbus-1-dev
|
||||
- pkg-config
|
||||
- cmake
|
||||
tags:
|
||||
- rust
|
||||
|
||||
- name: Download Installer for rust
|
||||
ansible.builtin.get_url:
|
||||
url: https://sh.rustup.rs
|
||||
dest: /tmp/sh.rustup.rs
|
||||
mode: '0755'
|
||||
force: 'yes'
|
||||
tags:
|
||||
- rust
|
||||
|
||||
- name: Install rust
|
||||
ansible.builtin.shell: /tmp/sh.rustup.rs -y
|
||||
tags:
|
||||
- rust
|
||||
|
||||
- name: Change user shell to zsh
|
||||
become: true
|
||||
ansible.builtin.user:
|
||||
name: '{{ ansible_user_id }}'
|
||||
shell: /bin/zsh
|
||||
tags:
|
||||
- zsh
|
||||
|
||||
- name: Update font cache
|
||||
ansible.builtin.shell: fc-cache -f -v
|
||||
tags:
|
||||
- fonts
|
||||
|
||||
- name: Install flatpak required packages
|
||||
become: true
|
||||
ansible.builtin.apt:
|
||||
pkg:
|
||||
- flatpak
|
||||
- gnome-software-plugin-flatpak
|
||||
- libgtk-3-dev
|
||||
tags:
|
||||
- flatpak
|
||||
|
||||
- name: Add flathub repo
|
||||
ansible.builtin.shell: flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
|
||||
tags:
|
||||
- flatpak
|
||||
|
||||
- name: Install github required packages
|
||||
become: true
|
||||
ansible.builtin.apt:
|
||||
pkg:
|
||||
- gh
|
||||
tags:
|
||||
- github
|
||||
|
||||
# - name: Add github copilot extension
|
||||
# ansible.builtin.shell: gh extension install github/gh-copilot
|
||||
# tags:
|
||||
# - github
|
||||
|
||||
- name: Install Docker required packages
|
||||
become: true
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- apt-transport-https
|
||||
- ca-certificates
|
||||
- curl
|
||||
- gnupg2
|
||||
- postgresql-client
|
||||
tags:
|
||||
- docker
|
||||
- name: Add Docker repository
|
||||
ansible.builtin.deb822_repository:
|
||||
name: docker
|
||||
uris: https://download.docker.com/linux/debian
|
||||
types: deb
|
||||
suites: trixie
|
||||
architectures: amd64
|
||||
components: stable
|
||||
signed_by: https://download.docker.com/linux/debian/gpg
|
||||
tags:
|
||||
- docker
|
||||
- name: Install Docker Engine
|
||||
become: true
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- docker-ce
|
||||
- docker-ce-cli
|
||||
- containerd.io
|
||||
update_cache: true
|
||||
tags:
|
||||
- docker
|
||||
|
||||
- name: Add user to docker group
|
||||
become: true
|
||||
ansible.builtin.user:
|
||||
name: '{{ ansible_user_id }}'
|
||||
groups: docker
|
||||
tags:
|
||||
- docker
|
||||
|
||||
- name: Start Docker service
|
||||
become: true
|
||||
ansible.builtin.service:
|
||||
name: docker
|
||||
state: started
|
||||
tags:
|
||||
- docker
|
||||
1
requirements.txt
Normal file
1
requirements.txt
Normal file
@@ -0,0 +1 @@
|
||||
pre-commit
|
||||
Reference in New Issue
Block a user