From b81e193056130d7bc902819448a49c29b1ac9b6e Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 17:24:24 -0800 Subject: [PATCH 01/12] feat(install): add script skeleton with colors and helpers --- install.sh | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 install.sh diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..f7e4256 --- /dev/null +++ b/install.sh @@ -0,0 +1,43 @@ +#!/bin/sh +set -eu + +# --- Constants --- +BASE_URL="https://github.com/mesa-dot-dev/git-fs/releases/latest/download" +DEFAULT_INSTALL_DIR="/usr/local/bin" +AUTO_YES=false +TMPDIR="" + +# --- Colors (only when stdout is a terminal) --- +if [ -t 1 ]; then + RED='\033[0;31m' + GREEN='\033[0;32m' + CYAN='\033[0;36m' + YELLOW='\033[0;33m' + BOLD='\033[1m' + RESET='\033[0m' +else + RED='' GREEN='' CYAN='' YELLOW='' BOLD='' RESET='' +fi + +# --- Output helpers --- +info() { printf "${CYAN}-->${RESET} %s\n" "$1"; } +success() { printf "${GREEN}-->${RESET} %s\n" "$1"; } +warn() { printf "${YELLOW}-->${RESET} %s\n" "$1" >&2; } +error() { printf "${RED}-->${RESET} %s\n" "$1" >&2; exit 1; } + +# --- Cleanup --- +cleanup() { + if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then + rm -rf "$TMPDIR" + fi +} +trap cleanup EXIT + +# --- Sudo wrapper --- +sudo_cmd() { + if [ "$(id -u)" -eq 0 ]; then + "$@" + else + sudo "$@" + fi +} From 229eacabb84384ad9a8c3456d576e519eb180ed3 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 17:24:42 -0800 Subject: [PATCH 02/12] feat(install): add argument parsing and dependency checks --- install.sh | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/install.sh b/install.sh index f7e4256..d71734c 100644 --- a/install.sh +++ b/install.sh @@ -41,3 +41,29 @@ sudo_cmd() { sudo "$@" fi } + +# --- Argument parsing --- +parse_args() { + while [ $# -gt 0 ]; do + case "$1" in + -y|--yes) AUTO_YES=true ;; + -h|--help) + printf "Usage: install.sh [OPTIONS]\n\n" + printf "Install or update git-fs.\n\n" + printf "Options:\n" + printf " -y, --yes Non-interactive mode (use defaults, requires root)\n" + printf " -h, --help Show this help message\n" + exit 0 + ;; + *) error "Unknown option: $1 (use -h for help)" ;; + esac + shift + done +} + +# --- Dependency checks --- +check_deps() { + if ! command -v curl >/dev/null 2>&1; then + error "curl is required but not found. Install it with your package manager." + fi +} From 4d72c252f543def60aa6e3fe1e5c1fdeaf64fb39 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 17:24:59 -0800 Subject: [PATCH 03/12] feat(install): add OS and architecture detection --- install.sh | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/install.sh b/install.sh index d71734c..b6270d3 100644 --- a/install.sh +++ b/install.sh @@ -67,3 +67,31 @@ check_deps() { error "curl is required but not found. Install it with your package manager." fi } + +# --- OS detection --- +detect_os() { + case "$(uname -s)" in + Darwin) + printf "\n" + info "${BOLD}git-fs installer${RESET}" + printf "\n" + warn "macOS is not yet supported by this install script." + info "Download manually from:" + info " ${BASE_URL}/git-fs-macos-universal.tar.gz" + printf "\n" + exit 0 + ;; + Linux) OS=linux ;; + *) error "Unsupported operating system: $(uname -s)" ;; + esac +} + +# --- Architecture detection --- +detect_arch() { + case "$(uname -m)" in + x86_64) ARCH_DEB=amd64; ARCH_RPM=x86_64; ARCH_TAR=amd64 ;; + aarch64) ARCH_DEB=arm64; ARCH_RPM=aarch64; ARCH_TAR=arm64 ;; + arm64) ARCH_DEB=arm64; ARCH_RPM=aarch64; ARCH_TAR=arm64 ;; + *) error "Unsupported architecture: $(uname -m)" ;; + esac +} From 8f215c1dabcbe7fac6f68f58ecc25c35abe2c7b4 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 17:25:20 -0800 Subject: [PATCH 04/12] feat(install): add distro detection and mapping --- install.sh | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/install.sh b/install.sh index b6270d3..1b5d60f 100644 --- a/install.sh +++ b/install.sh @@ -95,3 +95,54 @@ detect_arch() { *) error "Unsupported architecture: $(uname -m)" ;; esac } + +# --- Distro detection --- +detect_distro() { + PKG_TYPE="" + DISTRO="" + + if [ ! -f /etc/os-release ]; then + PKG_TYPE=tarball + return + fi + + . /etc/os-release + + case "$ID" in + debian) + case "$VERSION_ID" in + 12|13) DISTRO="debian-${VERSION_ID}"; PKG_TYPE=deb ;; + *) PKG_TYPE=tarball ;; + esac + ;; + ubuntu) + case "$VERSION_ID" in + 20.04|22.04|24.04) DISTRO="ubuntu-${VERSION_ID}"; PKG_TYPE=deb ;; + *) PKG_TYPE=tarball ;; + esac + ;; + rocky) + MAJOR=$(echo "$VERSION_ID" | cut -d. -f1) + case "$MAJOR" in + 8|9) DISTRO="rocky-${MAJOR}"; PKG_TYPE=rpm ;; + *) PKG_TYPE=tarball ;; + esac + ;; + almalinux) + MAJOR=$(echo "$VERSION_ID" | cut -d. -f1) + case "$MAJOR" in + 8|9) DISTRO="alma-${MAJOR}"; PKG_TYPE=rpm ;; + *) PKG_TYPE=tarball ;; + esac + ;; + fedora) + case "$VERSION_ID" in + 40|41|42|43) DISTRO="fedora-${VERSION_ID}"; PKG_TYPE=rpm ;; + *) PKG_TYPE=tarball ;; + esac + ;; + *) + PKG_TYPE=tarball + ;; + esac +} From 293f5ff3fd5328b4d272631a9c3e8b5e3f6b6cf1 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 17:25:39 -0800 Subject: [PATCH 05/12] feat(install): add tarball fallback with interactive prompts --- install.sh | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/install.sh b/install.sh index 1b5d60f..58e2537 100644 --- a/install.sh +++ b/install.sh @@ -146,3 +146,42 @@ detect_distro() { ;; esac } + +# --- Tarball fallback prompting --- +prompt_tarball_install() { + INSTALL_DIR="$DEFAULT_INSTALL_DIR" + + if [ "$AUTO_YES" = true ]; then + info "No native package for this distro. Installing generic Linux binary to ${INSTALL_DIR}." + return + fi + + printf "\n" + if [ -n "${ID:-}" ]; then + warn "No native package available for ${ID} ${VERSION_ID:-}." + else + warn "Could not detect your Linux distribution." + fi + printf " Would you like to install the generic Linux binary instead? [Y/n] " + read -r answer Date: Wed, 4 Feb 2026 17:26:02 -0800 Subject: [PATCH 06/12] feat(install): add download, install, verify, and main flow --- install.sh | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/install.sh b/install.sh index 58e2537..1587745 100644 --- a/install.sh +++ b/install.sh @@ -185,3 +185,93 @@ prompt_tarball_install() { sudo_cmd mkdir -p "$INSTALL_DIR" fi } + +# --- Build download URL --- +build_url() { + case "$PKG_TYPE" in + deb) + FILENAME="git-fs_${DISTRO}_${ARCH_DEB}.deb" + ;; + rpm) + FILENAME="git-fs_${DISTRO}.${ARCH_RPM}.rpm" + ;; + tarball) + FILENAME="git-fs-linux-${ARCH_TAR}.tar.gz" + ;; + esac + URL="${BASE_URL}/${FILENAME}" +} + +# --- Download --- +download() { + TMPDIR=$(mktemp -d) + info "Downloading ${FILENAME}..." + curl -fSL -o "${TMPDIR}/${FILENAME}" "$URL" || error "Download failed. Check your internet connection and try again." +} + +# --- Install --- +install_package() { + case "$PKG_TYPE" in + deb) + info "Installing deb package..." + sudo_cmd apt install -y "${TMPDIR}/${FILENAME}" + ;; + rpm) + info "Installing rpm package..." + if command -v dnf >/dev/null 2>&1; then + sudo_cmd dnf install -y "${TMPDIR}/${FILENAME}" + elif command -v yum >/dev/null 2>&1; then + sudo_cmd yum install -y "${TMPDIR}/${FILENAME}" + else + error "Neither dnf nor yum found. Cannot install rpm package." + fi + ;; + tarball) + info "Installing binary to ${INSTALL_DIR}..." + tar -xzf "${TMPDIR}/${FILENAME}" -C "$TMPDIR" + sudo_cmd install -m 755 "${TMPDIR}/git-fs" "${INSTALL_DIR}/git-fs" + warn "Note: git-fs requires FUSE3. Install it with your package manager if not already present." + ;; + esac +} + +# --- Verify --- +verify_install() { + if command -v git-fs >/dev/null 2>&1; then + VERSION=$(git-fs --version 2>/dev/null || echo "unknown") + success "git-fs installed successfully! (${VERSION})" + else + success "git-fs installed successfully!" + fi +} + +# --- Main --- +main() { + parse_args "$@" + check_deps + + printf "\n" + info "${BOLD}git-fs installer${RESET}" + printf "\n" + + detect_os + detect_arch + detect_distro + + if [ "$PKG_TYPE" = "tarball" ]; then + prompt_tarball_install + fi + + # In non-interactive mode, require root + if [ "$AUTO_YES" = true ] && [ "$(id -u)" -ne 0 ]; then + error "Non-interactive mode requires root. Re-run with: sudo $0 -y" + fi + + build_url + download + install_package + verify_install + printf "\n" +} + +main "$@" From 771d562da6a1309990d3400479072bfff1968b2d Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 17:39:41 -0800 Subject: [PATCH 07/12] fix(install): use %b format specifier for printf color handling --- install.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/install.sh b/install.sh index 1587745..6fb7bd1 100644 --- a/install.sh +++ b/install.sh @@ -20,10 +20,10 @@ else fi # --- Output helpers --- -info() { printf "${CYAN}-->${RESET} %s\n" "$1"; } -success() { printf "${GREEN}-->${RESET} %s\n" "$1"; } -warn() { printf "${YELLOW}-->${RESET} %s\n" "$1" >&2; } -error() { printf "${RED}-->${RESET} %s\n" "$1" >&2; exit 1; } +info() { printf "%b-->%b %b\n" "$CYAN" "$RESET" "$1"; } +success() { printf "%b-->%b %b\n" "$GREEN" "$RESET" "$1"; } +warn() { printf "%b-->%b %b\n" "$YELLOW" "$RESET" "$1" >&2; } +error() { printf "%b-->%b %b\n" "$RED" "$RESET" "$1" >&2; exit 1; } # --- Cleanup --- cleanup() { From bb6757976009fa19442a3d4c3f0bffd7729bcb09 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 17:40:41 -0800 Subject: [PATCH 08/12] chore: make install.sh executable --- install.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 install.sh diff --git a/install.sh b/install.sh old mode 100644 new mode 100755 From 16bae88f61b885c5749c35d723bdd64f9960cb2e Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 19:16:33 -0800 Subject: [PATCH 09/12] shellcheck --- .github/workflows/shellcheck.yml | 31 +++++ .github/workflows/test-install.yml | 120 ++++++++++++++++ install.sh | 217 ++++++++++++++++------------- 3 files changed, 270 insertions(+), 98 deletions(-) create mode 100644 .github/workflows/shellcheck.yml create mode 100644 .github/workflows/test-install.yml diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml new file mode 100644 index 0000000..c543b9d --- /dev/null +++ b/.github/workflows/shellcheck.yml @@ -0,0 +1,31 @@ +name: ShellCheck + +on: + push: + branches: [main] + paths: ["**.sh"] + pull_request: + branches: [main] + paths: ["**.sh"] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + shellcheck: + name: ShellCheck + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Run ShellCheck (strictest settings) + run: | + shellcheck --version + shellcheck \ + --shell=sh \ + --severity=style \ + --enable=all \ + --external-sources \ + --format=gcc \ + ./install.sh diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml new file mode 100644 index 0000000..f8e34be --- /dev/null +++ b/.github/workflows/test-install.yml @@ -0,0 +1,120 @@ +name: Test install.sh + +on: + push: + branches: [main] + paths: [install.sh] + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + # --- Syntax check and macOS early-exit --- + basic: + name: Basic checks + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Syntax check + run: sh -n install.sh + + - name: macOS early exit + run: | + output=$(sh install.sh 2>&1) + echo "$output" + echo "$output" | grep -q "macOS is not yet supported" + + - name: Help flag + run: | + output=$(sh install.sh -h 2>&1) + echo "$output" + echo "$output" | grep -q "Usage:" + + - name: Unknown flag + run: | + if sh install.sh --bogus 2>&1; then + echo "Expected non-zero exit"; exit 1 + fi + + # --- DEB distros --- + deb: + name: DEB · ${{ matrix.distro.name }} + runs-on: ubuntu-latest + container: + image: ${{ matrix.distro.image }} + options: --user root + strategy: + fail-fast: false + matrix: + distro: + - { name: debian-12, image: "debian:12" } + - { name: debian-13, image: "debian:trixie" } + - { name: ubuntu-20.04, image: "ubuntu:20.04" } + - { name: ubuntu-22.04, image: "ubuntu:22.04" } + - { name: ubuntu-24.04, image: "ubuntu:24.04" } + steps: + - name: Install curl + run: | + apt-get update + apt-get install -y curl + + - uses: actions/checkout@v4 + + - name: Run install.sh -y + run: sh install.sh -y + + - name: Verify git-fs is on PATH + run: git-fs --version + + # --- RPM distros --- + rpm: + name: RPM · ${{ matrix.distro.name }} + runs-on: ubuntu-latest + container: + image: ${{ matrix.distro.image }} + options: --user root + strategy: + fail-fast: false + matrix: + distro: + - { name: rocky-8, image: "rockylinux:8" } + - { name: rocky-9, image: "rockylinux:9" } + - { name: alma-8, image: "almalinux:8" } + - { name: alma-9, image: "almalinux:9" } + - { name: fedora-40, image: "fedora:40" } + - { name: fedora-41, image: "fedora:41" } + - { name: fedora-42, image: "fedora:42" } + - { name: fedora-43, image: "fedora:43" } + steps: + - name: Install curl and git + run: dnf install -y curl git + + - uses: actions/checkout@v4 + + - name: Run install.sh -y + run: sh install.sh -y + + - name: Verify git-fs is on PATH + run: git-fs --version + + # --- Tarball fallback (unsupported distro) --- + tarball: + name: Tarball · archlinux + runs-on: ubuntu-latest + container: + image: archlinux:latest + options: --user root + steps: + - name: Install dependencies + run: pacman -Sy --noconfirm curl git + + - uses: actions/checkout@v4 + + - name: Run install.sh -y + run: sh install.sh -y + + - name: Verify git-fs is on PATH + run: git-fs --version diff --git a/install.sh b/install.sh index 6fb7bd1..573a93c 100755 --- a/install.sh +++ b/install.sh @@ -1,4 +1,5 @@ #!/bin/sh +# shellcheck disable=SC1091 # /etc/os-release is not available at lint time set -eu # --- Constants --- @@ -20,22 +21,23 @@ else fi # --- Output helpers --- -info() { printf "%b-->%b %b\n" "$CYAN" "$RESET" "$1"; } -success() { printf "%b-->%b %b\n" "$GREEN" "$RESET" "$1"; } -warn() { printf "%b-->%b %b\n" "$YELLOW" "$RESET" "$1" >&2; } -error() { printf "%b-->%b %b\n" "$RED" "$RESET" "$1" >&2; exit 1; } +info() { printf "%b-->%b %b\n" "${CYAN}" "${RESET}" "$1"; } +success() { printf "%b-->%b %b\n" "${GREEN}" "${RESET}" "$1"; } +warn() { printf "%b-->%b %b\n" "${YELLOW}" "${RESET}" "$1" >&2; } +error() { printf "%b-->%b %b\n" "${RED}" "${RESET}" "$1" >&2; exit 1; } # --- Cleanup --- cleanup() { - if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then - rm -rf "$TMPDIR" + if [ -n "${TMPDIR}" ] && [ -d "${TMPDIR}" ]; then + rm -rf "${TMPDIR}" fi } trap cleanup EXIT # --- Sudo wrapper --- sudo_cmd() { - if [ "$(id -u)" -eq 0 ]; then + _uid=$(id -u) + if [ "${_uid}" -eq 0 ]; then "$@" else sudo "$@" @@ -70,176 +72,179 @@ check_deps() { # --- OS detection --- detect_os() { - case "$(uname -s)" in + _os=$(uname -s) + case "${_os}" in Darwin) - printf "\n" - info "${BOLD}git-fs installer${RESET}" - printf "\n" warn "macOS is not yet supported by this install script." info "Download manually from:" info " ${BASE_URL}/git-fs-macos-universal.tar.gz" printf "\n" exit 0 ;; - Linux) OS=linux ;; - *) error "Unsupported operating system: $(uname -s)" ;; + Linux) ;; + *) error "Unsupported operating system: ${_os}" ;; esac } -# --- Architecture detection --- +# --- Architecture detection (prints: deb_arch rpm_arch tar_arch) --- detect_arch() { - case "$(uname -m)" in - x86_64) ARCH_DEB=amd64; ARCH_RPM=x86_64; ARCH_TAR=amd64 ;; - aarch64) ARCH_DEB=arm64; ARCH_RPM=aarch64; ARCH_TAR=arm64 ;; - arm64) ARCH_DEB=arm64; ARCH_RPM=aarch64; ARCH_TAR=arm64 ;; - *) error "Unsupported architecture: $(uname -m)" ;; + _machine=$(uname -m) + case "${_machine}" in + x86_64) echo "amd64 x86_64 amd64" ;; + aarch64) echo "arm64 aarch64 arm64" ;; + arm64) echo "arm64 aarch64 arm64" ;; + *) error "Unsupported architecture: ${_machine}" ;; esac } -# --- Distro detection --- +# --- Distro detection (prints: pkg_type distro_name distro_id distro_version) --- detect_distro() { - PKG_TYPE="" - DISTRO="" - if [ ! -f /etc/os-release ]; then - PKG_TYPE=tarball + echo "tarball" return fi - . /etc/os-release + # shellcheck disable=SC2154 # ID and VERSION_ID are defined by os-release + _distro_id=$(. /etc/os-release && echo "${ID}") + _version_id=$(. /etc/os-release && echo "${VERSION_ID:-}") - case "$ID" in + case "${_distro_id}" in debian) - case "$VERSION_ID" in - 12|13) DISTRO="debian-${VERSION_ID}"; PKG_TYPE=deb ;; - *) PKG_TYPE=tarball ;; + case "${_version_id}" in + 12|13) echo "deb debian-${_version_id} ${_distro_id} ${_version_id}" ;; + *) echo "tarball _ ${_distro_id} ${_version_id}" ;; esac ;; ubuntu) - case "$VERSION_ID" in - 20.04|22.04|24.04) DISTRO="ubuntu-${VERSION_ID}"; PKG_TYPE=deb ;; - *) PKG_TYPE=tarball ;; + case "${_version_id}" in + 20.04|22.04|24.04) echo "deb ubuntu-${_version_id} ${_distro_id} ${_version_id}" ;; + *) echo "tarball _ ${_distro_id} ${_version_id}" ;; esac ;; rocky) - MAJOR=$(echo "$VERSION_ID" | cut -d. -f1) - case "$MAJOR" in - 8|9) DISTRO="rocky-${MAJOR}"; PKG_TYPE=rpm ;; - *) PKG_TYPE=tarball ;; + _major=$(echo "${_version_id}" | cut -d. -f1) + case "${_major}" in + 8|9) echo "rpm rocky-${_major} ${_distro_id} ${_version_id}" ;; + *) echo "tarball _ ${_distro_id} ${_version_id}" ;; esac ;; almalinux) - MAJOR=$(echo "$VERSION_ID" | cut -d. -f1) - case "$MAJOR" in - 8|9) DISTRO="alma-${MAJOR}"; PKG_TYPE=rpm ;; - *) PKG_TYPE=tarball ;; + _major=$(echo "${_version_id}" | cut -d. -f1) + case "${_major}" in + 8|9) echo "rpm alma-${_major} ${_distro_id} ${_version_id}" ;; + *) echo "tarball _ ${_distro_id} ${_version_id}" ;; esac ;; fedora) - case "$VERSION_ID" in - 40|41|42|43) DISTRO="fedora-${VERSION_ID}"; PKG_TYPE=rpm ;; - *) PKG_TYPE=tarball ;; + case "${_version_id}" in + 40|41|42|43) echo "rpm fedora-${_version_id} ${_distro_id} ${_version_id}" ;; + *) echo "tarball _ ${_distro_id} ${_version_id}" ;; esac ;; *) - PKG_TYPE=tarball + echo "tarball _ ${_distro_id} ${_version_id}" ;; esac } -# --- Tarball fallback prompting --- +# --- Tarball fallback prompting (prints: install_dir) --- prompt_tarball_install() { - INSTALL_DIR="$DEFAULT_INSTALL_DIR" + _auto_yes="$1" _distro_id="$2" _distro_ver="$3" + _install_dir="${DEFAULT_INSTALL_DIR}" - if [ "$AUTO_YES" = true ]; then - info "No native package for this distro. Installing generic Linux binary to ${INSTALL_DIR}." + if [ "${_auto_yes}" = true ]; then + info "No native package for this distro. Installing generic Linux binary to ${_install_dir}." >&2 + echo "${_install_dir}" return fi - printf "\n" - if [ -n "${ID:-}" ]; then - warn "No native package available for ${ID} ${VERSION_ID:-}." + printf "\n" >&2 + if [ "${_distro_id}" != "_" ] && [ -n "${_distro_id}" ]; then + warn "No native package available for ${_distro_id} ${_distro_ver}." else warn "Could not detect your Linux distribution." fi - printf " Would you like to install the generic Linux binary instead? [Y/n] " + printf " Would you like to install the generic Linux binary instead? [Y/n] " >&2 read -r answer &2; exit 0 ;; + *) ;; esac - printf " Install location [${DEFAULT_INSTALL_DIR}]: " + printf " Install location [%s]: " "${DEFAULT_INSTALL_DIR}" >&2 read -r custom_dir &2 read -r create_answer /dev/null 2>&1; then - sudo_cmd dnf install -y "${TMPDIR}/${FILENAME}" + sudo_cmd dnf install -y "${_filepath}" elif command -v yum >/dev/null 2>&1; then - sudo_cmd yum install -y "${TMPDIR}/${FILENAME}" + sudo_cmd yum install -y "${_filepath}" else error "Neither dnf nor yum found. Cannot install rpm package." fi ;; tarball) - info "Installing binary to ${INSTALL_DIR}..." - tar -xzf "${TMPDIR}/${FILENAME}" -C "$TMPDIR" - sudo_cmd install -m 755 "${TMPDIR}/git-fs" "${INSTALL_DIR}/git-fs" + info "Installing binary to ${_install_dir}..." + tar -xzf "${_filepath}" -C "$(dirname "${_filepath}")" + sudo_cmd install -m 755 "$(dirname "${_filepath}")/git-fs" "${_install_dir}/git-fs" warn "Note: git-fs requires FUSE3. Install it with your package manager if not already present." ;; + *) + error "Unknown package type: ${_pkg_type}" + ;; esac } # --- Verify --- verify_install() { if command -v git-fs >/dev/null 2>&1; then - VERSION=$(git-fs --version 2>/dev/null || echo "unknown") - success "git-fs installed successfully! (${VERSION})" + _version=$(git-fs --version 2>/dev/null || echo "unknown") + success "git-fs installed successfully! (${_version})" else success "git-fs installed successfully!" fi @@ -255,21 +260,37 @@ main() { printf "\n" detect_os - detect_arch - detect_distro - if [ "$PKG_TYPE" = "tarball" ]; then - prompt_tarball_install + # -- Detect environment -- + arch=$(detect_arch) + arch_deb=$(echo "${arch}" | cut -d' ' -f1) + arch_rpm=$(echo "${arch}" | cut -d' ' -f2) + arch_tar=$(echo "${arch}" | cut -d' ' -f3) + + distro_info=$(detect_distro) + pkg_type=$(echo "${distro_info}" | cut -d' ' -f1) + distro=$(echo "${distro_info}" | cut -d' ' -f2) + distro_id=$(echo "${distro_info}" | cut -d' ' -f3) + distro_ver=$(echo "${distro_info}" | cut -d' ' -f4) + + # -- Tarball fallback -- + install_dir="${DEFAULT_INSTALL_DIR}" + if [ "${pkg_type}" = "tarball" ]; then + install_dir=$(prompt_tarball_install "${AUTO_YES}" "${distro_id:-_}" "${distro_ver:-}") fi - # In non-interactive mode, require root - if [ "$AUTO_YES" = true ] && [ "$(id -u)" -ne 0 ]; then + # -- Require root in non-interactive mode -- + _uid=$(id -u) + if [ "${AUTO_YES}" = true ] && [ "${_uid}" -ne 0 ]; then error "Non-interactive mode requires root. Re-run with: sudo $0 -y" fi - build_url - download - install_package + # -- Build URL, download, install -- + filename=$(build_filename "${pkg_type}" "${distro}" "${arch_deb}" "${arch_rpm}" "${arch_tar}") + url="${BASE_URL}/${filename}" + + download "${filename}" "${url}" + install_package "${pkg_type}" "${TMPDIR}/${filename}" "${install_dir}" verify_install printf "\n" } From dc64af73aec827cdda97592c4d0d4b8413646246 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 19:18:28 -0800 Subject: [PATCH 10/12] deslop --- install.sh | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/install.sh b/install.sh index 573a93c..80d6c7d 100755 --- a/install.sh +++ b/install.sh @@ -2,13 +2,11 @@ # shellcheck disable=SC1091 # /etc/os-release is not available at lint time set -eu -# --- Constants --- BASE_URL="https://github.com/mesa-dot-dev/git-fs/releases/latest/download" DEFAULT_INSTALL_DIR="/usr/local/bin" AUTO_YES=false TMPDIR="" -# --- Colors (only when stdout is a terminal) --- if [ -t 1 ]; then RED='\033[0;31m' GREEN='\033[0;32m' @@ -20,13 +18,11 @@ else RED='' GREEN='' CYAN='' YELLOW='' BOLD='' RESET='' fi -# --- Output helpers --- info() { printf "%b-->%b %b\n" "${CYAN}" "${RESET}" "$1"; } success() { printf "%b-->%b %b\n" "${GREEN}" "${RESET}" "$1"; } warn() { printf "%b-->%b %b\n" "${YELLOW}" "${RESET}" "$1" >&2; } error() { printf "%b-->%b %b\n" "${RED}" "${RESET}" "$1" >&2; exit 1; } -# --- Cleanup --- cleanup() { if [ -n "${TMPDIR}" ] && [ -d "${TMPDIR}" ]; then rm -rf "${TMPDIR}" @@ -34,7 +30,6 @@ cleanup() { } trap cleanup EXIT -# --- Sudo wrapper --- sudo_cmd() { _uid=$(id -u) if [ "${_uid}" -eq 0 ]; then @@ -44,7 +39,6 @@ sudo_cmd() { fi } -# --- Argument parsing --- parse_args() { while [ $# -gt 0 ]; do case "$1" in @@ -63,14 +57,12 @@ parse_args() { done } -# --- Dependency checks --- check_deps() { if ! command -v curl >/dev/null 2>&1; then error "curl is required but not found. Install it with your package manager." fi } -# --- OS detection --- detect_os() { _os=$(uname -s) case "${_os}" in @@ -86,7 +78,6 @@ detect_os() { esac } -# --- Architecture detection (prints: deb_arch rpm_arch tar_arch) --- detect_arch() { _machine=$(uname -m) case "${_machine}" in @@ -97,7 +88,6 @@ detect_arch() { esac } -# --- Distro detection (prints: pkg_type distro_name distro_id distro_version) --- detect_distro() { if [ ! -f /etc/os-release ]; then echo "tarball" @@ -147,7 +137,6 @@ detect_distro() { esac } -# --- Tarball fallback prompting (prints: install_dir) --- prompt_tarball_install() { _auto_yes="$1" _distro_id="$2" _distro_ver="$3" _install_dir="${DEFAULT_INSTALL_DIR}" @@ -190,7 +179,6 @@ prompt_tarball_install() { echo "${_install_dir}" } -# --- Build filename (prints: filename) --- build_filename() { _pkg_type="$1" _distro="$2" _arch_deb="$3" _arch_rpm="$4" _arch_tar="$5" case "${_pkg_type}" in @@ -201,7 +189,6 @@ build_filename() { esac } -# --- Download (sets global TMPDIR for cleanup trap) --- download() { _filename="$1" _url="$2" TMPDIR=$(mktemp -d) @@ -210,7 +197,6 @@ download() { || error "Download failed. Check your internet connection and try again." } -# --- Install (takes: pkg_type filepath [install_dir]) --- install_package() { _pkg_type="$1" _filepath="$2" _install_dir="${3:-}" case "${_pkg_type}" in @@ -240,7 +226,6 @@ install_package() { esac } -# --- Verify --- verify_install() { if command -v git-fs >/dev/null 2>&1; then _version=$(git-fs --version 2>/dev/null || echo "unknown") @@ -250,7 +235,6 @@ verify_install() { fi } -# --- Main --- main() { parse_args "$@" check_deps @@ -261,7 +245,6 @@ main() { detect_os - # -- Detect environment -- arch=$(detect_arch) arch_deb=$(echo "${arch}" | cut -d' ' -f1) arch_rpm=$(echo "${arch}" | cut -d' ' -f2) @@ -273,19 +256,16 @@ main() { distro_id=$(echo "${distro_info}" | cut -d' ' -f3) distro_ver=$(echo "${distro_info}" | cut -d' ' -f4) - # -- Tarball fallback -- install_dir="${DEFAULT_INSTALL_DIR}" if [ "${pkg_type}" = "tarball" ]; then install_dir=$(prompt_tarball_install "${AUTO_YES}" "${distro_id:-_}" "${distro_ver:-}") fi - # -- Require root in non-interactive mode -- _uid=$(id -u) if [ "${AUTO_YES}" = true ] && [ "${_uid}" -ne 0 ]; then error "Non-interactive mode requires root. Re-run with: sudo $0 -y" fi - # -- Build URL, download, install -- filename=$(build_filename "${pkg_type}" "${distro}" "${arch_deb}" "${arch_rpm}" "${arch_tar}") url="${BASE_URL}/${filename}" From 40da26df4ea57c69a0494b87d9786c95a3cb98b9 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 19:22:51 -0800 Subject: [PATCH 11/12] test install.sh on PR --- .github/workflows/test-install.yml | 34 +++--------------------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index f8e34be..030bcb1 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -4,6 +4,9 @@ on: push: branches: [main] paths: [install.sh] + pull_request: + branches: [main] + paths: [install.sh] workflow_dispatch: concurrency: @@ -11,35 +14,6 @@ concurrency: cancel-in-progress: true jobs: - # --- Syntax check and macOS early-exit --- - basic: - name: Basic checks - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - - name: Syntax check - run: sh -n install.sh - - - name: macOS early exit - run: | - output=$(sh install.sh 2>&1) - echo "$output" - echo "$output" | grep -q "macOS is not yet supported" - - - name: Help flag - run: | - output=$(sh install.sh -h 2>&1) - echo "$output" - echo "$output" | grep -q "Usage:" - - - name: Unknown flag - run: | - if sh install.sh --bogus 2>&1; then - echo "Expected non-zero exit"; exit 1 - fi - - # --- DEB distros --- deb: name: DEB · ${{ matrix.distro.name }} runs-on: ubuntu-latest @@ -69,7 +43,6 @@ jobs: - name: Verify git-fs is on PATH run: git-fs --version - # --- RPM distros --- rpm: name: RPM · ${{ matrix.distro.name }} runs-on: ubuntu-latest @@ -100,7 +73,6 @@ jobs: - name: Verify git-fs is on PATH run: git-fs --version - # --- Tarball fallback (unsupported distro) --- tarball: name: Tarball · archlinux runs-on: ubuntu-latest From 080d90d0ac305f588f6c8cbbd792732776f14b00 Mon Sep 17 00:00:00 2001 From: Marko Vejnovic Date: Wed, 4 Feb 2026 19:29:27 -0800 Subject: [PATCH 12/12] fix CI --- .github/workflows/test-install.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-install.yml b/.github/workflows/test-install.yml index 030bcb1..76282c9 100644 --- a/.github/workflows/test-install.yml +++ b/.github/workflows/test-install.yml @@ -63,7 +63,7 @@ jobs: - { name: fedora-43, image: "fedora:43" } steps: - name: Install curl and git - run: dnf install -y curl git + run: dnf install -y --allowerasing curl git - uses: actions/checkout@v4 @@ -88,5 +88,5 @@ jobs: - name: Run install.sh -y run: sh install.sh -y - - name: Verify git-fs is on PATH - run: git-fs --version + - name: Verify git-fs was installed + run: test -x /usr/local/bin/git-fs