Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
go:
name: Go build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.21'
cache: true
cache-dependency-path: cli/go.sum

- name: Build
working-directory: cli
run: go build ./...

- name: Vet
working-directory: cli
run: go vet ./...

python:
name: Python syntax
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: py_compile every script
run: |
set -e
for f in scripts/*.py cli/_vintage_helper.py; do
echo "Checking $f"
python3 -m py_compile "$f"
done

shell:
name: Shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install shellcheck
run: sudo apt-get update && sudo apt-get install -y shellcheck

- name: Check install/uninstall scripts
run: |
shellcheck -e SC2086 -e SC1091 -e SC2154 \
cli/install.sh cli/uninstall.sh \
cli/install-vintage.sh cli/uninstall-vintage.sh
168 changes: 85 additions & 83 deletions cli/install.sh
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
#!/usr/bin/env bash
# install.sh — installer for hypercar-colorscripts (v0.3 with vintage opt-in)
# install.sh — installer for hypercar-colorscripts (v0.3.2)
#
# Asks LOGIN-shell detected user one question per concern:
# - default rendering mode (auto/ansi)
# - whether to include vintage cars (pre-2000)
# - whether to also install into a secondary rc file (e.g. .bashrc)
# - final go/no-go before modifying any rc file
#
# Vintage cars are managed by _vintage_helper.py. After installation,
# the user can add or remove vintage cars without rerunning install.sh:
# ./install-vintage.sh # add vintage
# ./uninstall-vintage.sh # remove vintage
# Removed: shell tab completion installation (bash/zsh/fish).
# Everything else preserved from v0.3.1.
#
# Flags:
# --system binary -> /usr/local/bin (sudo)
# --no-shell skip all .rc modifications
# --yes / -y skip confirmations; defaults to modern-only, auto mode
# --yes / -y skip confirmations
# --with-vintage include vintage cars even with --yes
# --no-mix don't mix pokemon even if pokemon-colorscripts is present
# --uninstall forward to uninstall.sh

set -uo pipefail
Expand All @@ -28,13 +21,14 @@ warn() { echo -e "${YELLOW}⚠${NC} $*"; }
fail() { echo -e "${RED}✗${NC} $*" >&2; exit 1; }
fsize() { stat -c %s "$1" 2>/dev/null || stat -f %z "$1" 2>/dev/null || echo 0; }

SYSTEM_INSTALL=0; NO_SHELL=0; ASSUME_YES=0; WITH_VINTAGE=0
SYSTEM_INSTALL=0; NO_SHELL=0; ASSUME_YES=0; WITH_VINTAGE=0; NO_MIX=0
for arg in "$@"; do
case "$arg" in
--system) SYSTEM_INSTALL=1 ;;
--no-shell) NO_SHELL=1 ;;
--yes|-y) ASSUME_YES=1 ;;
--with-vintage) WITH_VINTAGE=1 ;;
--no-mix) NO_MIX=1 ;;
--uninstall) exec "$(dirname "${BASH_SOURCE[0]}")/uninstall.sh" ;;
--help|-h) sed -n '2,/^set -uo/p' "$0" | sed 's/^# \?//'; exit 0 ;;
esac
Expand All @@ -52,63 +46,57 @@ HELPER="$SCRIPT_DIR/_vintage_helper.py"

echo -e "${BLUE}═══ hypercar-colorscripts installer ═══${NC}"

# ─── 1. Prereqs ─────────────────────────────────
# 1. Prereqs
step "1/8 Prerequisites"
command -v chafa >/dev/null 2>&1 || fail "chafa required — sudo dnf install chafa"
ok "chafa"

PYTHON="$(command -v python3 || command -v python || true)"
[[ -n "$PYTHON" ]] || fail "python required"
ok "python: $PYTHON"

[[ -f "$HELPER" ]] || fail "missing helper: $HELPER"

# ─── 2. Build ───────────────────────────────────
HAS_POKEMON=0
if command -v pokemon-colorscripts >/dev/null 2>&1; then
HAS_POKEMON=1
ok "pokemon-colorscripts detected at $(command -v pokemon-colorscripts)"
fi

# 2. Build
step "2/8 Binary"
[[ -x "$SCRIPT_DIR/$BINARY" ]] || (cd "$SCRIPT_DIR" && make build) || fail "make build failed"
ok "$SCRIPT_DIR/$BINARY"

# ─── 3. Source data ─────────────────────────────
# 3. Source data
step "3/8 Source data"
[[ -f "$SRC_MANIFEST" ]] || fail "manifest.json missing — run scripts/build_manifest.py"
[[ -f "$SRC_MANIFEST" ]] || fail "manifest.json missing"
[[ -d "$SRC_ASSETS/images" ]] || fail "assets/images/ missing"
ok "$(ls "$SRC_ASSETS/images" | wc -l) PNGs in source"

# Inspect source via helper
eval "$($PYTHON "$HELPER" info "$SRC_MANIFEST")" || fail "helper info failed"
VINTAGE_MB=$((VINTAGE_BYTES / 1024 / 1024))
ok "Source: $MODERN_COUNT modern + $VINTAGE_COUNT vintage cars (pre-2000, ~${VINTAGE_MB} MB)"
ok "Source: $MODERN_COUNT modern + $VINTAGE_COUNT vintage cars (~${VINTAGE_MB} MB)"

# ─── 4. Default rendering mode ──────────────────
# 4. Rendering mode
step "4/8 Default rendering mode"
DEFAULT_MODE="auto"
if [[ $ASSUME_YES -eq 0 ]]; then
echo "Choose how cars/pokemon render at terminal startup:"
echo " 1) auto — real image in Kitty/Ghostty/WezTerm, ANSI text elsewhere (recommended)"
echo " 2) ansi — always pre-rendered text (any terminal, including git bash)"
echo "How to render at terminal startup:"
echo " 1) auto — real image in Kitty/Ghostty/WezTerm, ANSI text elsewhere"
echo " 2) ansi — always pre-rendered text"
read -rp "Choice [1]: " m
case "${m:-1}" in 2) DEFAULT_MODE="ansi" ;; *) DEFAULT_MODE="auto" ;; esac
fi
ok "Mode: $DEFAULT_MODE (change later: hypercar-colorscripts --set-mode auto|ansi)"
ok "Mode: $DEFAULT_MODE"

# ─── 5. Vintage choice ──────────────────────────
# 5. Vintage
step "5/8 Vintage cars"

# Detect previous install state so default flips intelligently
PREV_HAS_VINTAGE=0
if [[ -f "$DATA_DIR/manifest.json" ]]; then
eval "$($PYTHON "$HELPER" status "$DATA_DIR")"
PREV_HAS_VINTAGE=${HAS_VINTAGE:-0}
fi

INCLUDE_VINTAGE=$WITH_VINTAGE
if [[ $ASSUME_YES -eq 1 ]]; then
# --yes: respect --with-vintage flag, otherwise default to modern-only
:
else
if [[ $ASSUME_YES -eq 0 ]]; then
echo "Include $VINTAGE_COUNT vintage cars (pre-2000)? Adds ~${VINTAGE_MB} MB."
if [[ $PREV_HAS_VINTAGE -eq 1 ]]; then
echo "(Currently installed with vintage)"
read -rp "Include vintage? [Y/n]: " ans
[[ "${ans:-Y}" =~ ^[Nn] ]] && INCLUDE_VINTAGE=0 || INCLUDE_VINTAGE=1
else
Expand All @@ -117,22 +105,38 @@ else
fi
fi

if [[ $INCLUDE_VINTAGE -eq 1 ]]; then
ok "Will install: all $TOTAL_COUNT cars"
# 6. Pokemon mixing
step "6/8 Pokemon mixing"
POKEMON_MIX="false"
if [[ $HAS_POKEMON -eq 1 && $NO_MIX -eq 0 ]]; then
if [[ $ASSUME_YES -eq 1 ]]; then
POKEMON_MIX="true"
ok "Pokemon mixing enabled (--yes)"
else
echo "pokemon-colorscripts is installed."
read -rp "Mix pokemon 50/50 with cars at terminal startup? [Y/n]: " ans
if [[ "${ans:-Y}" =~ ^[Nn] ]]; then
POKEMON_MIX="false"
ok "Pokemon mixing disabled (only cars will show)"
else
POKEMON_MIX="true"
ok "Pokemon mixing enabled"
fi
fi
elif [[ $NO_MIX -eq 1 ]]; then
ok "Pokemon mixing disabled (--no-mix)"
else
ok "Will install: $MODERN_COUNT modern cars (vintage available later via ./install-vintage.sh)"
ok "pokemon-colorscripts not installed — mixing not applicable"
fi
echo " (toggle later: hypercar-colorscripts --set-mix on|off)"

# ─── 6. Install assets + config + binary ────────
step "6/8 Install assets, config, binary"
# 7. Assets + config + binary
step "7/8 Install assets, config, binary"
mkdir -p "$DATA_DIR"

if [[ $INCLUDE_VINTAGE -eq 1 ]]; then
"$PYTHON" "$HELPER" install-all "$SRC_MANIFEST" "$SRC_ASSETS" "$DATA_DIR" \
|| fail "install-all failed"
"$PYTHON" "$HELPER" install-all "$SRC_MANIFEST" "$SRC_ASSETS" "$DATA_DIR" || fail "install-all failed"
else
"$PYTHON" "$HELPER" install-modern "$SRC_MANIFEST" "$SRC_ASSETS" "$DATA_DIR" \
|| fail "install-modern failed"
"$PYTHON" "$HELPER" install-modern "$SRC_MANIFEST" "$SRC_ASSETS" "$DATA_DIR" || fail "install-modern failed"
fi
ok "Assets in $DATA_DIR"

Expand All @@ -141,6 +145,7 @@ cat > "$CONFIG_FILE" <<EOF
{
"data_dir": "$DATA_DIR",
"default_mode": "$DEFAULT_MODE",
"pokemon_mix": $POKEMON_MIX,
"with_pokemon": false,
"pokemon_ratio": 0.5,
"show_attribution": false
Expand All @@ -158,8 +163,8 @@ else
fi
ok "Binary: $BIN_DIR/$BINARY"

# ─── 7. Shell rc detection ──────────────────────
step "7/8 Shell rc detection"
# 8. Shell rc
step "8/8 Shell startup"
[[ $NO_SHELL -eq 1 ]] && { warn "Skipped (--no-shell)"; exit 0; }

detect_login_shell() {
Expand All @@ -180,41 +185,35 @@ declare -a RC_FILES=()
case "$LOGIN_SHELL" in
*/zsh) PRIMARY_RC="$HOME/.zshrc" ;;
*/bash) PRIMARY_RC="$HOME/.bashrc" ;;
*) PRIMARY_RC="$HOME/.profile"; warn "Unknown shell, using ~/.profile" ;;
*) PRIMARY_RC="$HOME/.profile" ;;
esac
[[ -f "$PRIMARY_RC" ]] || touch "$PRIMARY_RC"
RC_FILES+=("$PRIMARY_RC")
ok "Primary rc: $PRIMARY_RC"

SECONDARY_RC=""
if [[ "$PRIMARY_RC" == "$HOME/.zshrc" && -f "$HOME/.bashrc" ]]; then SECONDARY_RC="$HOME/.bashrc"
elif [[ "$PRIMARY_RC" == "$HOME/.bashrc" && -f "$HOME/.zshrc" ]]; then SECONDARY_RC="$HOME/.zshrc"
fi
if [[ -n "$SECONDARY_RC" ]]; then
if [[ $ASSUME_YES -eq 1 ]]; then
RC_FILES+=("$SECONDARY_RC"); ok "Also installing in: $SECONDARY_RC (--yes)"
RC_FILES+=("$SECONDARY_RC")
else
read -rp "Also install in $SECONDARY_RC? [Y/n]: " ans
[[ "${ans:-Y}" =~ ^[Yy] ]] && RC_FILES+=("$SECONDARY_RC")
fi
fi

if [[ $ASSUME_YES -eq 0 ]]; then
echo
echo "Will modify: ${RC_FILES[*]}"
read -rp "Proceed? [Y/n]: " ans
[[ ! "${ans:-Y}" =~ ^[Yy]$ ]] && { warn "Skipped."; exit 0; }
fi

# ─── 8. Apply rc modifications ──────────────────
step "8/8 Apply rc modifications"
for RC in "${RC_FILES[@]}"; do
echo
echo "Processing $RC"
test -w "$RC" || fail "$RC not writable. chmod u+w $RC"

test -w "$RC" || fail "$RC not writable"
BACKUP="$RC.hypercar-backup-$(date +%Y%m%d-%H%M%S)"
cp "$RC" "$BACKUP" || fail "backup failed"
cp "$RC" "$BACKUP"

sed -i.tmp '/# >>> hypercar-colorscripts PATH >>>/,/# <<< hypercar-colorscripts PATH <<</d' "$RC"
sed -i.tmp '/# >>> hypercar-colorscripts >>>/,/# <<< hypercar-colorscripts <<</d' "$RC"
Expand All @@ -223,18 +222,21 @@ for RC in "${RC_FILES[@]}"; do
if grep -qE '^[[:space:]]*pokemon-colorscripts' "$RC"; then
sed -i.tmp -E 's|^([[:space:]]*)(pokemon-colorscripts.*)|\1# hypercar-installer-disabled: \2|' "$RC"
rm -f "$RC.tmp"
ok "Commented out existing pokemon-colorscripts line"
ok "Commented existing pokemon line"
fi

if ! grep -qE '(\$HOME|\$\{HOME\}|~)/\.local/bin' "$RC"; then
if ! grep -qE "(\\$HOME|\\$\\{HOME\\}|~)/\\.local/bin" "$RC"; then
TMP="$(mktemp)"
{
printf '%s\n' '# >>> hypercar-colorscripts PATH >>>'
printf '%s\n' 'export PATH="$HOME/.local/bin:$PATH"'
printf '%s\n' "export PATH=\"\$HOME/.local/bin:\$PATH\""
printf '%s\n' '# <<< hypercar-colorscripts PATH <<<'
printf '\n'
cat "$RC"
} > "$TMP" && mv "$TMP" "$RC" || fail "PATH write failed"
} > "$TMP"
if ! mv "$TMP" "$RC"; then
fail "PATH write failed"
fi
ok "Added PATH export"
fi

Expand All @@ -248,19 +250,24 @@ __HC_CFG="$HOME/.config/fastfetch/config-pokemon.jsonc"
__HC_CONF="$HOME/.config/hypercar-colorscripts/config.json"

__HC_MODE="auto"
__HC_MIX="true"
if [ -f "$__HC_CONF" ]; then
__HC_MODE=$(sed -n 's/.*"default_mode"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$__HC_CONF" 2>/dev/null | head -1)
[ -z "$__HC_MODE" ] && __HC_MODE="auto"
__HC_TMP=$(sed -n 's/.*"default_mode"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$__HC_CONF" 2>/dev/null | head -1)
[ -n "$__HC_TMP" ] && __HC_MODE="$__HC_TMP"
__HC_TMP=$(sed -n 's/.*"pokemon_mix"[[:space:]]*:[[:space:]]*\(true\|false\).*/\1/p' "$__HC_CONF" 2>/dev/null | head -1)
[ -n "$__HC_TMP" ] && __HC_MIX="$__HC_TMP"
unset __HC_TMP
fi

__HC_IMG_OK=0
case "${TERM:-}" in xterm-kitty*) __HC_IMG_OK=1 ;; esac
[ -n "${KITTY_WINDOW_ID:-}" ] && __HC_IMG_OK=1
[ "${TERM_PROGRAM:-}" = "WezTerm" ] && __HC_IMG_OK=1
[ -n "${GHOSTTY_RESOURCES_DIR:-}" ] && __HC_IMG_OK=1
[ -n "${KITTY_WINDOW_ID:-}" ] && __HC_IMG_OK=1
[ "${TERM_PROGRAM:-}" = "WezTerm" ] && __HC_IMG_OK=1
[ "${TERM_PROGRAM:-}" = "iTerm.app" ] && __HC_IMG_OK=1
[ -n "${GHOSTTY_RESOURCES_DIR:-}" ] && __HC_IMG_OK=1
[ "$__HC_MODE" = "ansi" ] && __HC_IMG_OK=0

if [ $(($RANDOM % 2)) -eq 0 ] && command -v pokemon-colorscripts >/dev/null 2>&1; then
if [ "$__HC_MIX" = "true" ] && [ $(($RANDOM % 2)) -eq 0 ] && command -v pokemon-colorscripts >/dev/null 2>&1; then
if command -v fastfetch >/dev/null 2>&1 && [ -f "$__HC_CFG" ]; then
pokemon-colorscripts --no-title -s -r | \
fastfetch -c "$__HC_CFG" --logo-type file-raw --logo-height 10 --logo-width 5 --logo -
Expand All @@ -281,27 +288,22 @@ else
fi
fi
fi
unset __HC_DATA __HC_CFG __HC_CONF __HC_MODE __HC_IMG_OK __HC_IMG __HC_ANSI
unset __HC_DATA __HC_CFG __HC_CONF __HC_MODE __HC_MIX __HC_IMG_OK __HC_IMG __HC_ANSI
# <<< hypercar-colorscripts <<<
HC_BLOCK_END
AFTER=$(fsize "$RC")

[[ $AFTER -gt $BEFORE ]] || fail "$RC didn't grow ($BEFORE -> $AFTER bytes)"
grep -q '# >>> hypercar-colorscripts >>>' "$RC" || fail "$RC missing marker after append"
ok "Block appended ($((AFTER - BEFORE)) bytes, line $(grep -n '# >>> hypercar-colorscripts >>>' "$RC" | head -1 | cut -d: -f1))"
ok "Backup: $BACKUP"
[[ $AFTER -gt $BEFORE ]] || fail "$RC didn't grow"
grep -q '# >>> hypercar-colorscripts >>>' "$RC" || fail "$RC missing marker"
ok "Block appended ($((AFTER - BEFORE)) bytes)"
done

echo
echo -e "${GREEN}═══ Done ═══${NC}"
[[ $INCLUDE_VINTAGE -eq 1 ]] && \
echo "Installed: $TOTAL_COUNT cars (modern + vintage)" || \
echo "Installed: $MODERN_COUNT cars (modern only)"
echo "Mode: $DEFAULT_MODE (toggle with: hypercar-colorscripts --set-mode auto|ansi)"
echo "Rc files: ${RC_FILES[*]}"
[[ $INCLUDE_VINTAGE -eq 1 ]] && echo "Installed: $TOTAL_COUNT cars (modern + vintage)" || echo "Installed: $MODERN_COUNT cars"
echo "Mode: $DEFAULT_MODE Pokemon mix: $POKEMON_MIX"
echo
echo "Vintage management (run from this repo):"
echo " ./install-vintage.sh add vintage cars"
echo " ./uninstall-vintage.sh remove vintage cars"
echo "Toggle later:"
echo " hypercar-colorscripts --set-mode auto|ansi"
echo " hypercar-colorscripts --set-mix on|off"
echo
echo "Open a new terminal to see the changes."
Loading
Loading