Skip to content

chore(deps): update reviewdog/action-setup action to v1.5.0#843

Open
renovate[bot] wants to merge 1 commit intomainfrom
renovate/reviewdog-action-setup-1-x
Open

chore(deps): update reviewdog/action-setup action to v1.5.0#843
renovate[bot] wants to merge 1 commit intomainfrom
renovate/reviewdog-action-setup-1-x

Conversation

@renovate
Copy link
Copy Markdown
Contributor

@renovate renovate bot commented Sep 15, 2025

This PR contains the following updates:

Package Type Update Change
reviewdog/action-setup action minor v1.3.2v1.5.0

Release Notes

reviewdog/action-setup (reviewdog/action-setup)

v1.5.0

Compare Source

What's Changed

Full Changelog: reviewdog/action-setup@v1.4.0...v1.5.0

v1.4.0

Compare Source

What's Changed

Full Changelog: reviewdog/action-setup@v1.3.2...v1.4.0


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate bot requested a review from a team as a code owner September 15, 2025 16:04
@thypon thypon force-pushed the main branch 4 times, most recently from c9cb82f to df38488 Compare September 24, 2025 15:50
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from 8cce20e to 982349d Compare September 25, 2025 18:32
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from 982349d to b8c217a Compare October 6, 2025 12:41
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from b8c217a to b075aca Compare October 9, 2025 10:51
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from b075aca to 49a0e32 Compare November 10, 2025 19:15
@github-actions
Copy link
Copy Markdown
Contributor

[puLL-Merge] - reviewdog/action-setup@v1.3.2..v1.4.0

Diff
diff --git a/.config/binstaller/reviewdog-nightly.yml b/.config/binstaller/reviewdog-nightly.yml
new file mode 100644
index 0000000..3f4427d
--- /dev/null
+++ .config/binstaller/reviewdog-nightly.yml
@@ -0,0 +1,23 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
+schema: v1
+repo: reviewdog/nightly
+name: reviewdog
+asset:
+  template: reviewdog_${VERSION}_${OS}_${ARCH}${EXT}
+  default_extension: .tar.gz
+  rules:
+  - when:
+      arch: amd64
+    arch: x86_64
+  - when:
+      os: darwin
+    os: Darwin
+  - when:
+      os: linux
+    os: Linux
+  - when:
+      os: windows
+    os: Windows
+checksums:
+  algorithm: sha256
+  template: checksums.txt
diff --git a/.config/binstaller/reviewdog.yml b/.config/binstaller/reviewdog.yml
new file mode 100644
index 0000000..759b32e
--- /dev/null
+++ .config/binstaller/reviewdog.yml
@@ -0,0 +1,48 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
+# TODO: Generate install.sh from GitHub Actions and support gh attestation.
+# https://github.com/binary-install/binstaller is still WIP but it should be ok
+# since it's built and maintained by me (haya14busa, a maintainer of reviewdog).
+# The installer script works fine and fixed bugs which existed in the previous
+# script generated by godownloader. Example: The previous script didn't work if
+# release artifacts contains <binary-asset>.sbom.json since the prev script
+# just used grep with <binary-asset> without '$'.
+schema: v1
+name: reviewdog
+repo: reviewdog/reviewdog
+asset:
+  template: ${NAME}_${VERSION}_${OS}_${ARCH}${EXT}
+  default_extension: .tar.gz
+  rules:
+  - when:
+      arch: amd64
+    arch: x86_64
+  - when:
+      arch: "386"
+    arch: i386
+  naming_convention:
+    os: titlecase
+    arch: lowercase
+checksums:
+  algorithm: sha256
+  template: checksums.txt
+supported_platforms:
+- os: darwin
+  arch: amd64
+- os: darwin
+  arch: arm64
+- os: linux
+  arch: "386"
+- os: linux
+  arch: amd64
+- os: linux
+  arch: arm64
+- os: linux
+  arch: armv6
+- os: windows
+  arch: "386"
+- os: windows
+  arch: amd64
+- os: windows
+  arch: arm64
+- os: windows
+  arch: armv6
diff --git .github/workflows/depup.yml .github/workflows/depup.yml
index 95cd2f1..9c00249 100644
--- .github/workflows/depup.yml
+++ .github/workflows/depup.yml
@@ -11,7 +11,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
-      - uses: haya14busa/action-depup@680d0a6dedb9c170ec21b8e1eacf7a1133937743 # v1.6.2
+      - uses: haya14busa/action-depup@94a1aaf4e4923064019214b48a43276218af7ad5 # v1.6.4
         id: depup
         with:
           file: README.md
diff --git .github/workflows/reviewdog.yml .github/workflows/reviewdog.yml
index 95b880e..d82e830 100644
--- .github/workflows/reviewdog.yml
+++ .github/workflows/reviewdog.yml
@@ -16,7 +16,7 @@ jobs:
           cond: ${{ github.event_name == 'pull_request' }}
           if_true: "github-pr-review"
           if_false: "github-check"
-      - uses: reviewdog/action-shellcheck@6e0e63d1750d02d761b3df0f2c5ba9f9ac4a9ed7 # v1.29.0
+      - uses: reviewdog/action-shellcheck@5ebd09ddbe2ebb471646ce234c6c8dd18663ca7c # v1.30.0
         with:
           github_token: ${{ secrets.github_token }}
           reporter: ${{ steps.reporter.outputs.value }}
@@ -57,7 +57,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
-      - uses: reviewdog/action-misspell@18ffb61effb93b47e332f185216be7e49592e7e1 # v1.26.1
+      - uses: reviewdog/action-misspell@9daa94af4357dddb6fd3775de806bc0a8e98d3e4 # v1.26.3
         with:
           github_token: ${{ secrets.github_token }}
           reporter: github-check
@@ -69,7 +69,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
-      - uses: reviewdog/action-alex@986cf7dd82e702f82b4173deaa793a849f5b719d # v1.15.0
+      - uses: reviewdog/action-alex@6083b8ca333981fa617c6828c5d8fb21b13d916b # v1.16.0
         with:
           github_token: ${{ secrets.github_token }}
           reporter: github-check
@@ -80,7 +80,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
-      - uses: reviewdog/action-actionlint@db58217885f9a6570da9c71be4e40ec33fe44a1f # v1.65.0
+      - uses: reviewdog/action-actionlint@a5524e1c19e62881d79c1f1b9b6f09f16356e281 # v1.65.2
         with:
           github_token: ${{ secrets.github_token }}
           reporter: github-check
diff --git README.md README.md
index d49e902..0a90aad 100644
--- README.md
+++ README.md
@@ -23,14 +23,14 @@ inputs:
 ### Latest
 ```yaml
 steps:
-  - uses: reviewdog/action-setup@v1
+  - uses: reviewdog/action-setup@e04ffabe3898a0af8d0fb1af00c188831c4b5893 # v1.3.2
   - run: reviewdog -version
 ```
 
 ### Specify reviewdog version
 ```yaml
 steps:
-  - uses: reviewdog/action-setup@v1
+  - uses: reviewdog/action-setup@e04ffabe3898a0af8d0fb1af00c188831c4b5893 # v1.3.2
     with:
       reviewdog_version: v0.20.3
   - run: reviewdog -version
@@ -39,7 +39,7 @@ steps:
 ### Nightly
 ```yaml
 steps:
-  - uses: reviewdog/action-setup@v1
+  - uses: reviewdog/action-setup@e04ffabe3898a0af8d0fb1af00c188831c4b5893 # v1.3.2
     with:
       reviewdog_version: nightly
   - run: reviewdog -version
diff --git a/install-nightly.sh b/install-nightly.sh
new file mode 100755
index 0000000..f0233f4
--- /dev/null
+++ install-nightly.sh
@@ -0,0 +1,554 @@
+#!/bin/sh
+# Code generated by binstaller. DO NOT EDIT.
+#
+set -e
+usage() {
+  this=$1
+  cat <<EOF
+$this: download ${NAME} from ${REPO}
+
+Usage: $this [-b bindir] [-d] [-n] [tag]
+  -b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
+  -d turns on debug logging
+  -n turns on dry run mode
+   [tag] is a tag from
+   https://github.com/reviewdog/nightly/releases
+   If tag is missing, then the latest will be used.
+
+ Generated by binstaller
+  https://github.com/binary-install/binstaller
+EOF
+  exit 2
+}
+
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+https://github.com/client9/shlib - portable posix shell functions
+Public domain - http://unlicense.org
+https://github.com/client9/shlib/blob/master/LICENSE.md
+but credit (and pull requests) appreciated.
+------------------------------------------------------------------------
+EOF
+is_command() {
+  command -v "$1" >/dev/null
+}
+echoerr() {
+  echo "$@" 1>&2
+}
+_logp=6
+log_set_priority() {
+  _logp="$1"
+}
+log_priority() {
+  if test -z "$1"; then
+    echo "$_logp"
+    return
+  fi
+  [ "$1" -le "$_logp" ]
+}
+log_tag() {
+  case $1 in
+    0) echo "emerg" ;;
+    1) echo "alert" ;;
+    2) echo "crit" ;;
+    3) echo "err" ;;
+    4) echo "warning" ;;
+    5) echo "notice" ;;
+    6) echo "info" ;;
+    7) echo "debug" ;;
+    *) echo "$1" ;;
+  esac
+}
+log_debug() {
+  log_priority 7 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
+}
+log_info() {
+  log_priority 6 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
+}
+log_err() {
+  log_priority 3 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
+}
+log_crit() {
+  log_priority 2 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
+}
+uname_os() {
+  os=$(uname -s | tr '[:upper:]' '[:lower:]')
+  case "$os" in
+    msys*) os="windows" ;;
+    mingw*) os="windows" ;;
+    cygwin*) os="windows" ;;
+  esac
+  if [ "$os" = "sunos" ]; then
+    if [ "$(uname -o)" = "illumos" ]; then
+      os="illumos"
+    else
+      os="solaris"
+    fi
+  fi
+  echo "$os"
+}
+uname_arch() {
+  arch=$(uname -m)
+  case $arch in
+    x86_64) arch="amd64" ;;
+    i86pc) arch="amd64" ;;
+    x86) arch="386" ;;
+    i686) arch="386" ;;
+    i386) arch="386" ;;
+    aarch64) arch="arm64" ;;
+    armv5*) arch="armv5" ;;
+    armv6*) arch="armv6" ;;
+    armv7*) arch="armv7" ;;
+  esac
+  echo "${arch}"
+}
+uname_os_check() {
+  os=$(uname_os)
+  case "$os" in
+    darwin) return 0 ;;
+    dragonfly) return 0 ;;
+    freebsd) return 0 ;;
+    linux) return 0 ;;
+    android) return 0 ;;
+    midnightbsd) return 0 ;;
+    nacl) return 0 ;;
+    netbsd) return 0 ;;
+    openbsd) return 0 ;;
+    plan9) return 0 ;;
+    solaris) return 0 ;;
+    illumos) return 0 ;;
+    windows) return 0 ;;
+  esac
+  log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
+  return 1
+}
+uname_arch_check() {
+  arch=$(uname_arch)
+  case "$arch" in
+    386) return 0 ;;
+    amd64) return 0 ;;
+    arm64) return 0 ;;
+    armv5) return 0 ;;
+    armv6) return 0 ;;
+    armv7) return 0 ;;
+    ppc64) return 0 ;;
+    ppc64le) return 0 ;;
+    mips) return 0 ;;
+    mipsle) return 0 ;;
+    mips64) return 0 ;;
+    mips64le) return 0 ;;
+    s390x) return 0 ;;
+    amd64p32) return 0 ;;
+  esac
+  log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib"
+  return 1
+}
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+End of functions from https://github.com/client9/shlib
+------------------------------------------------------------------------
+EOF
+
+
+hash_sha256() {
+  TARGET=${1:-/dev/stdin}
+  if is_command gsha256sum; then
+    hash=$(gsha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command sha256sum; then
+    hash=$(sha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command shasum; then
+    hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command openssl; then
+    hash=$(openssl dgst -sha256 "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 2
+  else
+    log_crit "hash_sha256 unable to find command to compute sha-256 hash"
+    return 1
+  fi
+}
+
+hash_compute() {
+  hash_sha256 "$1"
+}
+
+
+untar() {
+  tarball=$1
+  strip_components=${2:-0} # default 0
+  case "${tarball}" in
+  *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
+  *.gz) gunzip "${tarball}" ;;
+  *.zip)
+    # unzip doesn't have a standard --strip-components
+    # Workaround: extract to a subdir and move contents up if stripping
+    if [ "$strip_components" -gt 0 ]; then
+      extract_dir=$(basename "${tarball%.zip}")_extracted
+      unzip -q "${tarball}" -d "${extract_dir}"
+      # Move contents of the *first* directory found inside extract_dir up
+      # This assumes wrap_in_directory=true convention
+      first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
+      if [ -n "$first_subdir" ]; then
+        # Move all contents (* includes hidden files)
+        mv "${first_subdir}"/* .
+        # Optionally remove the now-empty subdir and the extract_dir
+        rmdir "${first_subdir}"
+        rmdir "${extract_dir}"
+      else
+        log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
+        # Files are extracted in current dir anyway, proceed
+      fi
+    else
+      unzip -q "${tarball}"
+    fi
+    ;;
+  *)
+    log_err "untar unknown archive format for ${tarball}"
+    return 1
+    ;;
+  esac
+}
+
+
+
+hash_verify() {
+  TARGET_PATH=$1
+  SUMFILE=$2
+  if [ -z "${SUMFILE}" ]; then
+    log_err "hash_verify checksum file not specified in arg2"
+    return 1
+  fi
+  got=$(hash_compute "$TARGET_PATH")
+  if [ -z "${got}" ]; then
+    log_err "failed to calculate hash: ${TARGET_PATH}"
+    return 1
+  fi
+
+  BASENAME=${TARGET_PATH##*/}
+
+  # Check for line matches in checksum file
+  # Format: "<hash>  <filename>" or "<hash> *<filename>"
+  # Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
+  while IFS= read -r line || [ -n "$line" ]; do
+    # Normalize tabs to spaces
+    line=$(echo "$line" | tr '\t' ' ')
+
+    # Remove trailing spaces for hash-only line check
+    line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
+
+    # Check for hash-only line (no filename) - early return
+    if [ "$line_trimmed" = "$got" ]; then
+      return 0
+    fi
+
+    # Extract hash and filename parts
+    # First field is the hash, rest is filename (which may contain spaces)
+    line_hash=$(echo "$line" | cut -d' ' -f1)
+
+    # Skip if hash doesn't match
+    if [ "$line_hash" != "$got" ]; then
+      continue
+    fi
+
+    # Hash matches, now check filename
+    # Remove the hash part from the beginning of the line
+    line_rest="${line#"$got"}"
+    # Remove leading spaces
+    while [ "${line_rest#[ ]}" != "$line_rest" ]; do
+      line_rest="${line_rest#[ ]}"
+    done
+
+    # Remove leading asterisk if present (binary mode indicator)
+    if [ "${line_rest#\*}" != "$line_rest" ]; then
+      line_rest="${line_rest#\*}"
+    fi
+
+    # Extract just the filename without any path
+    line_filename="${line_rest##*/}"
+
+    # Check if the filename matches
+    if [ "$line_filename" = "$BASENAME" ]; then
+      return 0
+    fi
+  done < "$SUMFILE"
+
+  log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
+  log_err "  Expected hash: ${got}"
+  log_err "  Checksum file content:"
+  cat "$SUMFILE" >&2
+  return 1
+}
+
+# GitHub HTTP download functions with GITHUB_TOKEN support
+github_http_download_curl() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      curl -fsSL -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "$header" -o "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download_wget() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
+    else
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      wget -q -O "$local_file" "$source_url"
+    else
+      wget -q --header "$header" -O "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download() {
+  log_debug "github_http_download $2"
+  if is_command curl; then
+    github_http_download_curl "$@"
+    return
+  elif is_command wget; then
+    github_http_download_wget "$@"
+    return
+  fi
+  log_crit "github_http_download unable to find wget or curl"
+  return 1
+}
+github_http_copy() {
+  tmp=$(mktemp)
+  github_http_download "${tmp}" "$@" || return 1
+  body=$(cat "$tmp")
+  rm -f "${tmp}"
+  echo "$body"
+}
+github_release() {
+  owner_repo=$1
+  version=$2
+  test -z "$version" && version="latest"
+  giturl="https://github.com/${owner_repo}/releases/${version}"
+  json=$(github_http_copy "$giturl" "Accept:application/json")
+  test -z "$json" && return 1
+  version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
+  test -z "$version" && return 1
+  echo "$version"
+}
+
+# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
+EMBEDDED_CHECKSUMS=""
+
+# Find embedded checksum for a given version and filename
+find_embedded_checksum() {
+  version="$1"
+  filename="$2"
+  echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
+}
+parse_args() {
+  BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
+  DRY_RUN=0
+  while getopts "b:dqh?xn" arg; do
+    case "$arg" in
+    b) BINDIR="$OPTARG" ;;
+    d) log_set_priority 10 ;;
+    q) log_set_priority 3 ;;
+    h | \?) usage "$0" ;;
+    x) set -x ;;
+    n) DRY_RUN=1 ;;
+    esac
+  done
+  shift $((OPTIND - 1))
+  TAG="${1:-latest}"
+}
+tag_to_version() {
+  if [ "$TAG" = "latest" ]; then
+    log_info "checking GitHub for latest tag"
+    REALTAG=$(github_release "${REPO}" "${TAG}") && true
+    test -n "$REALTAG" || {
+      log_crit "Could not determine latest tag for ${REPO}"
+      exit 1
+    }
+  else
+    # Assume TAG is a valid tag/version string
+    REALTAG="$TAG"
+  fi
+  if test -z "$REALTAG"; then
+    log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
+    exit 1
+  fi
+  VERSION=${REALTAG#v} # Strip leading 'v'
+  TAG="$REALTAG"       # Use the resolved tag
+  log_info "Resolved version: ${VERSION} (tag: ${TAG})"
+}
+
+
+
+resolve_asset_filename() {
+
+  # --- Apply Rules ---
+  ASSET_FILENAME=""
+  if [ "${UNAME_ARCH}" = 'amd64' ] && true
+  then
+    ARCH='x86_64'
+  fi
+  if [ "${UNAME_OS}" = 'darwin' ] && true
+  then
+    OS='Darwin'
+  fi
+  if [ "${UNAME_OS}" = 'linux' ] && true
+  then
+    OS='Linux'
+  fi
+  if [ "${UNAME_OS}" = 'windows' ] && true
+  then
+    OS='Windows'
+  fi
+  if [ -z "${ASSET_FILENAME}" ]; then
+    ASSET_FILENAME="reviewdog_${VERSION}_${OS}_${ARCH}${EXT}"
+  fi
+}
+# Cleanup function to remove temporary files
+cleanup() {
+  if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
+    log_debug "Cleaning up temporary directory: $TMPDIR"
+    rm -rf -- "$TMPDIR"
+  fi
+}
+
+execute() {
+  STRIP_COMPONENTS=0
+  CHECKSUM_FILENAME="checksums.txt"
+
+  # --- Construct URLs ---
+  GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
+  ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
+  CHECKSUM_URL=""
+  if [ -n "$CHECKSUM_FILENAME" ]; then
+    CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
+  fi
+
+  # --- Download and Verify ---
+  TMPDIR=$(mktemp -d)
+  trap 'rm -rf -- "$TMPDIR"' EXIT HUP INT TERM
+  log_debug "Downloading files into ${TMPDIR}"
+  log_info "Downloading ${ASSET_URL}"
+  github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
+
+  # Try to find embedded checksum first
+  EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
+
+  if [ -n "$EMBEDDED_HASH" ]; then
+    log_info "Using embedded checksum for verification"
+
+    # Verify using embedded hash
+    got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
+    if [ "$got" != "$EMBEDDED_HASH" ]; then
+      log_crit "Checksum verification failed for ${ASSET_FILENAME}"
+      log_crit "Expected: ${EMBEDDED_HASH}"
+      log_crit "Got: ${got}"
+      return 1
+    fi
+    log_info "Checksum verification successful"
+  elif [ -n "$CHECKSUM_URL" ]; then
+    # Fall back to downloading checksum file
+    log_info "Downloading checksums from ${CHECKSUM_URL}"
+    github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
+    log_info "Verifying checksum ..."
+    hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
+  else
+    log_info "No checksum found, skipping verification."
+  fi
+
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    log_debug "Target is raw binary"
+  else
+    log_info "Extracting ${ASSET_FILENAME}..."
+    (cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
+  fi
+  BINARY_NAME='reviewdog'
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
+  else
+    BINARY_PATH="${TMPDIR}/reviewdog"
+  fi
+
+  if [ "${UNAME_OS}" = "windows" ]; then
+    case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
+    case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
+  fi
+
+  if [ ! -f "${BINARY_PATH}" ]; then
+    log_crit "Binary not found: ${BINARY_PATH}"
+    log_crit "Listing contents of ${TMPDIR} ..."
+    if command -v find >/dev/null 2>&1; then
+      cd "${TMPDIR}" && find .
+    else
+      cd "${TMPDIR}" && ls -R .
+    fi
+    return 1
+  fi
+  # Install the binary
+  INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
+
+  if [ "$DRY_RUN" = "1" ]; then
+    log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
+  else
+    log_info "Installing binary to ${INSTALL_PATH}"
+    test ! -d "${BINDIR}" && install -d "${BINDIR}"
+    install "${BINARY_PATH}" "${INSTALL_PATH}"
+    log_info "${BINARY_NAME} installation complete!"
+  fi
+}
+
+# --- Configuration  ---
+NAME='reviewdog'
+REPO='reviewdog/nightly'
+EXT='.tar.gz'
+
+# use in logging routines
+log_prefix() {
+  echo "${REPO}"
+}
+
+parse_args "$@"
+
+# --- Determine target platform ---
+OS="${BINSTALLER_OS:-$(uname_os)}"
+UNAME_OS="${OS}"
+
+ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
+UNAME_ARCH="${ARCH}"
+log_info "Detected Platform: ${OS}/${ARCH}"
+
+# --- Validate platform ---
+uname_os_check "$OS"
+uname_arch_check "$ARCH"
+
+tag_to_version
+
+resolve_asset_filename
+
+execute
diff --git a/install-reviewdog.sh b/install-reviewdog.sh
new file mode 100755
index 0000000..e8ce78e
--- /dev/null
+++ install-reviewdog.sh
@@ -0,0 +1,553 @@
+#!/bin/sh
+# Code generated by binstaller. DO NOT EDIT.
+#
+set -e
+usage() {
+  this=$1
+  cat <<EOF
+$this: download ${NAME} from ${REPO}
+
+Usage: $this [-b bindir] [-d] [-n] [tag]
+  -b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
+  -d turns on debug logging
+  -n turns on dry run mode
+   [tag] is a tag from
+   https://github.com/reviewdog/reviewdog/releases
+   If tag is missing, then the latest will be used.
+
+ Generated by binstaller
+  https://github.com/binary-install/binstaller
+EOF
+  exit 2
+}
+
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+https://github.com/client9/shlib - portable posix shell functions
+Public domain - http://unlicense.org
+https://github.com/client9/shlib/blob/master/LICENSE.md
+but credit (and pull requests) appreciated.
+------------------------------------------------------------------------
+EOF
+is_command() {
+  command -v "$1" >/dev/null
+}
+echoerr() {
+  echo "$@" 1>&2
+}
+_logp=6
+log_set_priority() {
+  _logp="$1"
+}
+log_priority() {
+  if test -z "$1"; then
+    echo "$_logp"
+    return
+  fi
+  [ "$1" -le "$_logp" ]
+}
+log_tag() {
+  case $1 in
+    0) echo "emerg" ;;
+    1) echo "alert" ;;
+    2) echo "crit" ;;
+    3) echo "err" ;;
+    4) echo "warning" ;;
+    5) echo "notice" ;;
+    6) echo "info" ;;
+    7) echo "debug" ;;
+    *) echo "$1" ;;
+  esac
+}
+log_debug() {
+  log_priority 7 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
+}
+log_info() {
+  log_priority 6 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
+}
+log_err() {
+  log_priority 3 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
+}
+log_crit() {
+  log_priority 2 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
+}
+uname_os() {
+  os=$(uname -s | tr '[:upper:]' '[:lower:]')
+  case "$os" in
+    msys*) os="windows" ;;
+    mingw*) os="windows" ;;
+    cygwin*) os="windows" ;;
+  esac
+  if [ "$os" = "sunos" ]; then
+    if [ "$(uname -o)" = "illumos" ]; then
+      os="illumos"
+    else
+      os="solaris"
+    fi
+  fi
+  echo "$os"
+}
+uname_arch() {
+  arch=$(uname -m)
+  case $arch in
+    x86_64) arch="amd64" ;;
+    i86pc) arch="amd64" ;;
+    x86) arch="386" ;;
+    i686) arch="386" ;;
+    i386) arch="386" ;;
+    aarch64) arch="arm64" ;;
+    armv5*) arch="armv5" ;;
+    armv6*) arch="armv6" ;;
+    armv7*) arch="armv7" ;;
+  esac
+  echo "${arch}"
+}
+uname_os_check() {
+  os=$(uname_os)
+  case "$os" in
+    darwin) return 0 ;;
+    dragonfly) return 0 ;;
+    freebsd) return 0 ;;
+    linux) return 0 ;;
+    android) return 0 ;;
+    midnightbsd) return 0 ;;
+    nacl) return 0 ;;
+    netbsd) return 0 ;;
+    openbsd) return 0 ;;
+    plan9) return 0 ;;
+    solaris) return 0 ;;
+    illumos) return 0 ;;
+    windows) return 0 ;;
+  esac
+  log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
+  return 1
+}
+uname_arch_check() {
+  arch=$(uname_arch)
+  case "$arch" in
+    386) return 0 ;;
+    amd64) return 0 ;;
+    arm64) return 0 ;;
+    armv5) return 0 ;;
+    armv6) return 0 ;;
+    armv7) return 0 ;;
+    ppc64) return 0 ;;
+    ppc64le) return 0 ;;
+    mips) return 0 ;;
+    mipsle) return 0 ;;
+    mips64) return 0 ;;
+    mips64le) return 0 ;;
+    s390x) return 0 ;;
+    amd64p32) return 0 ;;
+  esac
+  log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib"
+  return 1
+}
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+End of functions from https://github.com/client9/shlib
+------------------------------------------------------------------------
+EOF
+
+
+hash_sha256() {
+  TARGET=${1:-/dev/stdin}
+  if is_command gsha256sum; then
+    hash=$(gsha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command sha256sum; then
+    hash=$(sha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command shasum; then
+    hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command openssl; then
+    hash=$(openssl dgst -sha256 "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 2
+  else
+    log_crit "hash_sha256 unable to find command to compute sha-256 hash"
+    return 1
+  fi
+}
+
+hash_compute() {
+  hash_sha256 "$1"
+}
+
+
+untar() {
+  tarball=$1
+  strip_components=${2:-0} # default 0
+  case "${tarball}" in
+  *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
+  *.gz) gunzip "${tarball}" ;;
+  *.zip)
+    # unzip doesn't have a standard --strip-components
+    # Workaround: extract to a subdir and move contents up if stripping
+    if [ "$strip_components" -gt 0 ]; then
+      extract_dir=$(basename "${tarball%.zip}")_extracted
+      unzip -q "${tarball}" -d "${extract_dir}"
+      # Move contents of the *first* directory found inside extract_dir up
+      # This assumes wrap_in_directory=true convention
+      first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
+      if [ -n "$first_subdir" ]; then
+        # Move all contents (* includes hidden files)
+        mv "${first_subdir}"/* .
+        # Optionally remove the now-empty subdir and the extract_dir
+        rmdir "${first_subdir}"
+        rmdir "${extract_dir}"
+      else
+        log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
+        # Files are extracted in current dir anyway, proceed
+      fi
+    else
+      unzip -q "${tarball}"
+    fi
+    ;;
+  *)
+    log_err "untar unknown archive format for ${tarball}"
+    return 1
+    ;;
+  esac
+}
+
+
+
+hash_verify() {
+  TARGET_PATH=$1
+  SUMFILE=$2
+  if [ -z "${SUMFILE}" ]; then
+    log_err "hash_verify checksum file not specified in arg2"
+    return 1
+  fi
+  got=$(hash_compute "$TARGET_PATH")
+  if [ -z "${got}" ]; then
+    log_err "failed to calculate hash: ${TARGET_PATH}"
+    return 1
+  fi
+
+  BASENAME=${TARGET_PATH##*/}
+
+  # Check for line matches in checksum file
+  # Format: "<hash>  <filename>" or "<hash> *<filename>"
+  # Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
+  while IFS= read -r line || [ -n "$line" ]; do
+    # Normalize tabs to spaces
+    line=$(echo "$line" | tr '\t' ' ')
+
+    # Remove trailing spaces for hash-only line check
+    line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
+
+    # Check for hash-only line (no filename) - early return
+    if [ "$line_trimmed" = "$got" ]; then
+      return 0
+    fi
+
+    # Extract hash and filename parts
+    # First field is the hash, rest is filename (which may contain spaces)
+    line_hash=$(echo "$line" | cut -d' ' -f1)
+
+    # Skip if hash doesn't match
+    if [ "$line_hash" != "$got" ]; then
+      continue
+    fi
+
+    # Hash matches, now check filename
+    # Remove the hash part from the beginning of the line
+    line_rest="${line#"$got"}"
+    # Remove leading spaces
+    while [ "${line_rest#[ ]}" != "$line_rest" ]; do
+      line_rest="${line_rest#[ ]}"
+    done
+
+    # Remove leading asterisk if present (binary mode indicator)
+    if [ "${line_rest#\*}" != "$line_rest" ]; then
+      line_rest="${line_rest#\*}"
+    fi
+
+    # Extract just the filename without any path
+    line_filename="${line_rest##*/}"
+
+    # Check if the filename matches
+    if [ "$line_filename" = "$BASENAME" ]; then
+      return 0
+    fi
+  done < "$SUMFILE"
+
+  log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
+  log_err "  Expected hash: ${got}"
+  log_err "  Checksum file content:"
+  cat "$SUMFILE" >&2
+  return 1
+}
+
+# GitHub HTTP download functions with GITHUB_TOKEN support
+github_http_download_curl() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      curl -fsSL -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "$header" -o "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download_wget() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
+    else
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      wget -q -O "$local_file" "$source_url"
+    else
+      wget -q --header "$header" -O "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download() {
+  log_debug "github_http_download $2"
+  if is_command curl; then
+    github_http_download_curl "$@"
+    return
+  elif is_command wget; then
+    github_http_download_wget "$@"
+    return
+  fi
+  log_crit "github_http_download unable to find wget or curl"
+  return 1
+}
+github_http_copy() {
+  tmp=$(mktemp)
+  github_http_download "${tmp}" "$@" || return 1
+  body=$(cat "$tmp")
+  rm -f "${tmp}"
+  echo "$body"
+}
+github_release() {
+  owner_repo=$1
+  version=$2
+  test -z "$version" && version="latest"
+  giturl="https://github.com/${owner_repo}/releases/${version}"
+  json=$(github_http_copy "$giturl" "Accept:application/json")
+  test -z "$json" && return 1
+  version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
+  test -z "$version" && return 1
+  echo "$version"
+}
+
+# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
+EMBEDDED_CHECKSUMS=""
+
+# Find embedded checksum for a given version and filename
+find_embedded_checksum() {
+  version="$1"
+  filename="$2"
+  echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
+}
+parse_args() {
+  BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
+  DRY_RUN=0
+  while getopts "b:dqh?xn" arg; do
+    case "$arg" in
+    b) BINDIR="$OPTARG" ;;
+    d) log_set_priority 10 ;;
+    q) log_set_priority 3 ;;
+    h | \?) usage "$0" ;;
+    x) set -x ;;
+    n) DRY_RUN=1 ;;
+    esac
+  done
+  shift $((OPTIND - 1))
+  TAG="${1:-latest}"
+}
+tag_to_version() {
+  if [ "$TAG" = "latest" ]; then
+    log_info "checking GitHub for latest tag"
+    REALTAG=$(github_release "${REPO}" "${TAG}") && true
+    test -n "$REALTAG" || {
+      log_crit "Could not determine latest tag for ${REPO}"
+      exit 1
+    }
+  else
+    # Assume TAG is a valid tag/version string
+    REALTAG="$TAG"
+  fi
+  if test -z "$REALTAG"; then
+    log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
+    exit 1
+  fi
+  VERSION=${REALTAG#v} # Strip leading 'v'
+  TAG="$REALTAG"       # Use the resolved tag
+  log_info "Resolved version: ${VERSION} (tag: ${TAG})"
+}
+
+
+capitalize() {
+  input="$1"
+  first_char=$(printf "%s" "$input" | cut -c1)
+  first_upper=$(printf "%s" "$first_char" | tr '[:lower:]' '[:upper:]')
+  printf "%s%s\n" "$first_upper" "$(printf "%s" "$input" | cut -c2-)"
+}
+
+resolve_asset_filename() {
+
+  OS="$(capitalize "${OS}")"
+  # --- Apply Rules ---
+  ASSET_FILENAME=""
+  if [ "${UNAME_ARCH}" = 'amd64' ] && true
+  then
+    ARCH='x86_64'
+  fi
+  if [ "${UNAME_ARCH}" = '386' ] && true
+  then
+    ARCH='i386'
+  fi
+  if [ -z "${ASSET_FILENAME}" ]; then
+    ASSET_FILENAME="${NAME}_${VERSION}_${OS}_${ARCH}${EXT}"
+  fi
+}
+# Cleanup function to remove temporary files
+cleanup() {
+  if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
+    log_debug "Cleaning up temporary directory: $TMPDIR"
+    rm -rf -- "$TMPDIR"
+  fi
+}
+
+execute() {
+  STRIP_COMPONENTS=0
+  CHECKSUM_FILENAME="checksums.txt"
+
+  # --- Construct URLs ---
+  GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
+  ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
+  CHECKSUM_URL=""
+  if [ -n "$CHECKSUM_FILENAME" ]; then
+    CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
+  fi
+
+  # --- Download and Verify ---
+  TMPDIR=$(mktemp -d)
+  trap 'rm -rf -- "$TMPDIR"' EXIT HUP INT TERM
+  log_debug "Downloading files into ${TMPDIR}"
+  log_info "Downloading ${ASSET_URL}"
+  github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
+
+  # Try to find embedded checksum first
+  EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
+
+  if [ -n "$EMBEDDED_HASH" ]; then
+    log_info "Using embedded checksum for verification"
+
+    # Verify using embedded hash
+    got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
+    if [ "$got" != "$EMBEDDED_HASH" ]; then
+      log_crit "Checksum verification failed for ${ASSET_FILENAME}"
+      log_crit "Expected: ${EMBEDDED_HASH}"
+      log_crit "Got: ${got}"
+      return 1
+    fi
+    log_info "Checksum verification successful"
+  elif [ -n "$CHECKSUM_URL" ]; then
+    # Fall back to downloading checksum file
+    log_info "Downloading checksums from ${CHECKSUM_URL}"
+    github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
+    log_info "Verifying checksum ..."
+    hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
+  else
+    log_info "No checksum found, skipping verification."
+  fi
+
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    log_debug "Target is raw binary"
+  else
+    log_info "Extracting ${ASSET_FILENAME}..."
+    (cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
+  fi
+  BINARY_NAME='reviewdog'
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
+  else
+    BINARY_PATH="${TMPDIR}/reviewdog"
+  fi
+
+  if [ "${UNAME_OS}" = "windows" ]; then
+    case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
+    case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
+  fi
+
+  if [ ! -f "${BINARY_PATH}" ]; then
+    log_crit "Binary not found: ${BINARY_PATH}"
+    log_crit "Listing contents of ${TMPDIR} ..."
+    if command -v find >/dev/null 2>&1; then
+      cd "${TMPDIR}" && find .
+    else
+      cd "${TMPDIR}" && ls -R .
+    fi
+    return 1
+  fi
+  # Install the binary
+  INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
+
+  if [ "$DRY_RUN" = "1" ]; then
+    log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
+  else
+    log_info "Installing binary to ${INSTALL_PATH}"
+    test ! -d "${BINDIR}" && install -d "${BINDIR}"
+    install "${BINARY_PATH}" "${INSTALL_PATH}"
+    log_info "${BINARY_NAME} installation complete!"
+  fi
+}
+
+# --- Configuration  ---
+NAME='reviewdog'
+REPO='reviewdog/reviewdog'
+EXT='.tar.gz'
+
+# use in logging routines
+log_prefix() {
+  echo "${REPO}"
+}
+
+parse_args "$@"
+
+# --- Determine target platform ---
+OS="${BINSTALLER_OS:-$(uname_os)}"
+UNAME_OS="${OS}"
+
+ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
+UNAME_ARCH="${ARCH}"
+log_info "Detected Platform: ${OS}/${ARCH}"
+
+# --- Validate platform ---
+uname_os_check "$OS"
+uname_arch_check "$ARCH"
+
+tag_to_version
+
+resolve_asset_filename
+
+execute
diff --git install.sh install.sh
index 6738103..728bfef 100755
--- install.sh
+++ install.sh
@@ -13,25 +13,16 @@ if [ -z "${TEMP}" ]; then
   fi
 fi
 
-INSTALL_SCRIPT='https://raw.githubusercontent.com/reviewdog/reviewdog/fd59714416d6d9a1c0692d872e38e7f8448df4fc/install.sh'
+INSTALL_SCRIPT="$GITHUB_ACTION_PATH/install-reviewdog.sh"
 if [ "${VERSION}" = 'nightly' ]; then
-  INSTALL_SCRIPT='https://raw.githubusercontent.com/reviewdog/nightly/30fccfe9f47f7e6fd8b3c38aa0da11a6c9f04de7/install.sh'
+  INSTALL_SCRIPT="$GITHUB_ACTION_PATH/install-nightly.sh"
   VERSION='latest'
 fi
 
 mkdir -p "${TEMP}/reviewdog/bin"
 
 echo '::group::🐶 Installing reviewdog ... https://github.com/reviewdog/reviewdog'
-(
-  if command -v curl 2>&1 >/dev/null; then
-    curl -sfL "${INSTALL_SCRIPT}"
-  elif command -v wget 2>&1 >/dev/null; then
-    wget -O - "${INSTALL_SCRIPT}"
-  else
-    echo "curl or wget is required" >&2
-    exit 1
-  fi
-) | sh -s -- -b "${TEMP}/reviewdog/bin" "${VERSION}" 2>&1
+cat "${INSTALL_SCRIPT}" | sh -s -- -b "${TEMP}/reviewdog/bin" "${VERSION}" 2>&1
 echo '::endgroup::'
 
 echo "${TEMP}/reviewdog/bin" >>"${GITHUB_PATH}"
diff --git a/scripts/binst.sh b/scripts/binst.sh
new file mode 100755
index 0000000..867918f
--- /dev/null
+++ scripts/binst.sh
@@ -0,0 +1,574 @@
+#!/bin/sh
+# Code generated by binstaller. DO NOT EDIT.
+# This script runs binst directly without installing
+#
+set -e
+usage() {
+  this=$1
+  cat <<EOF
+$this: download and run ${NAME} from ${REPO}
+
+Usage: $this [-d] [tag] -- [binary arguments]
+  -d turns on debug logging
+   [tag] is a tag from
+   https://github.com/binary-install/binstaller/releases
+   If tag is missing, then the latest will be used.
+
+   This script downloads and runs binst directly
+   Pass arguments after --:
+   Example: $this -- --help
+   Example: $this -- --version
+
+ Generated by binstaller
+  https://github.com/binary-install/binstaller
+EOF
+  exit 2
+}
+
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+https://github.com/client9/shlib - portable posix shell functions
+Public domain - http://unlicense.org
+https://github.com/client9/shlib/blob/master/LICENSE.md
+but credit (and pull requests) appreciated.
+------------------------------------------------------------------------
+EOF
+is_command() {
+  command -v "$1" >/dev/null
+}
+echoerr() {
+  echo "$@" 1>&2
+}
+_logp=6
+log_set_priority() {
+  _logp="$1"
+}
+log_priority() {
+  if test -z "$1"; then
+    echo "$_logp"
+    return
+  fi
+  [ "$1" -le "$_logp" ]
+}
+log_tag() {
+  case $1 in
+    0) echo "emerg" ;;
+    1) echo "alert" ;;
+    2) echo "crit" ;;
+    3) echo "err" ;;
+    4) echo "warning" ;;
+    5) echo "notice" ;;
+    6) echo "info" ;;
+    7) echo "debug" ;;
+    *) echo "$1" ;;
+  esac
+}
+log_debug() {
+  log_priority 7 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
+}
+log_info() {
+  log_priority 6 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
+}
+log_err() {
+  log_priority 3 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
+}
+log_crit() {
+  log_priority 2 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
+}
+uname_os() {
+  os=$(uname -s | tr '[:upper:]' '[:lower:]')
+  case "$os" in
+    msys*) os="windows" ;;
+    mingw*) os="windows" ;;
+    cygwin*) os="windows" ;;
+  esac
+  if [ "$os" = "sunos" ]; then
+    if [ "$(uname -o)" = "illumos" ]; then
+      os="illumos"
+    else
+      os="solaris"
+    fi
+  fi
+  echo "$os"
+}
+uname_arch() {
+  arch=$(uname -m)
+  case $arch in
+    x86_64) arch="amd64" ;;
+    i86pc) arch="amd64" ;;
+    x86) arch="386" ;;
+    i686) arch="386" ;;
+    i386) arch="386" ;;
+    aarch64) arch="arm64" ;;
+    armv5*) arch="armv5" ;;
+    armv6*) arch="armv6" ;;
+    armv7*) arch="armv7" ;;
+  esac
+  echo "${arch}"
+}
+uname_os_check() {
+  os=$(uname_os)
+  case "$os" in
+    darwin) return 0 ;;
+    dragonfly) return 0 ;;
+    freebsd) return 0 ;;
+    linux) return 0 ;;
+    android) return 0 ;;
+    midnightbsd) return 0 ;;
+    nacl) return 0 ;;
+    netbsd) return 0 ;;
+    openbsd) return 0 ;;
+    plan9) return 0 ;;
+    solaris) return 0 ;;
+    illumos) return 0 ;;
+    windows) return 0 ;;
+  esac
+  log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
+  return 1
+}
+uname_arch_check() {
+  arch=$(uname_arch)
+  case "$arch" in
+    386) return 0 ;;
+    amd64) return 0 ;;
+    arm64) return 0 ;;
+    armv5) return 0 ;;
+    armv6) return 0 ;;
+    armv7) return 0 ;;
+    ppc64) return 0 ;;
+    ppc64le) return 0 ;;
+    mips) return 0 ;;
+    mipsle) return 0 ;;
+    mips64) return 0 ;;
+    mips64le) return 0 ;;
+    s390x) return 0 ;;
+    amd64p32) return 0 ;;
+  esac
+  log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib"
+  return 1
+}
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+End of functions from https://github.com/client9/shlib
+------------------------------------------------------------------------
+EOF
+
+
+hash_sha256() {
+  TARGET=${1:-/dev/stdin}
+  if is_command gsha256sum; then
+    hash=$(gsha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command sha256sum; then
+    hash=$(sha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command shasum; then
+    hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command openssl; then
+    hash=$(openssl dgst -sha256 "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 2
+  else
+    log_crit "hash_sha256 unable to find command to compute sha-256 hash"
+    return 1
+  fi
+}
+
+hash_compute() {
+  hash_sha256 "$1"
+}
+
+
+untar() {
+  tarball=$1
+  strip_components=${2:-0} # default 0
+  case "${tarball}" in
+  *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
+  *.gz) gunzip "${tarball}" ;;
+  *.zip)
+    # unzip doesn't have a standard --strip-components
+    # Workaround: extract to a subdir and move contents up if stripping
+    if [ "$strip_components" -gt 0 ]; then
+      extract_dir=$(basename "${tarball%.zip}")_extracted
+      unzip -q "${tarball}" -d "${extract_dir}"
+      # Move contents of the *first* directory found inside extract_dir up
+      # This assumes wrap_in_directory=true convention
+      first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
+      if [ -n "$first_subdir" ]; then
+        # Move all contents (* includes hidden files)
+        mv "${first_subdir}"/* .
+        # Optionally remove the now-empty subdir and the extract_dir
+        rmdir "${first_subdir}"
+        rmdir "${extract_dir}"
+      else
+        log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
+        # Files are extracted in current dir anyway, proceed
+      fi
+    else
+      unzip -q "${tarball}"
+    fi
+    ;;
+  *)
+    log_err "untar unknown archive format for ${tarball}"
+    return 1
+    ;;
+  esac
+}
+
+
+
+hash_verify() {
+  TARGET_PATH=$1
+  SUMFILE=$2
+  if [ -z "${SUMFILE}" ]; then
+    log_err "hash_verify checksum file not specified in arg2"
+    return 1
+  fi
+  got=$(hash_compute "$TARGET_PATH")
+  if [ -z "${got}" ]; then
+    log_err "failed to calculate hash: ${TARGET_PATH}"
+    return 1
+  fi
+
+  BASENAME=${TARGET_PATH##*/}
+
+  # Check for line matches in checksum file
+  # Format: "<hash>  <filename>" or "<hash> *<filename>"
+  # Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
+  while IFS= read -r line || [ -n "$line" ]; do
+    # Normalize tabs to spaces
+    line=$(echo "$line" | tr '\t' ' ')
+
+    # Remove trailing spaces for hash-only line check
+    line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
+
+    # Check for hash-only line (no filename) - early return
+    if [ "$line_trimmed" = "$got" ]; then
+      return 0
+    fi
+
+    # Extract hash and filename parts
+    # First field is the hash, rest is filename (which may contain spaces)
+    line_hash=$(echo "$line" | cut -d' ' -f1)
+
+    # Skip if hash doesn't match
+    if [ "$line_hash" != "$got" ]; then
+      continue
+    fi
+
+    # Hash matches, now check filename
+    # Remove the hash part from the beginning of the line
+    line_rest="${line#"$got"}"
+    # Remove leading spaces
+    while [ "${line_rest#[ ]}" != "$line_rest" ]; do
+      line_rest="${line_rest#[ ]}"
+    done
+
+    # Remove leading asterisk if present (binary mode indicator)
+    if [ "${line_rest#\*}" != "$line_rest" ]; then
+      line_rest="${line_rest#\*}"
+    fi
+
+    # Extract just the filename without any path
+    line_filename="${line_rest##*/}"
+
+    # Check if the filename matches
+    if [ "$line_filename" = "$BASENAME" ]; then
+      return 0
+    fi
+  done < "$SUMFILE"
+
+  log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
+  log_err "  Expected hash: ${got}"
+  log_err "  Checksum file content:"
+  cat "$SUMFILE" >&2
+  return 1
+}
+
+# GitHub HTTP download functions with GITHUB_TOKEN support
+github_http_download_curl() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      curl -fsSL -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "$header" -o "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download_wget() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
+    else
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      wget -q -O "$local_file" "$source_url"
+    else
+      wget -q --header "$header" -O "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download() {
+  log_debug "github_http_download $2"
+  if is_command curl; then
+    github_http_download_curl "$@"
+    return
+  elif is_command wget; then
+    github_http_download_wget "$@"
+    return
+  fi
+  log_crit "github_http_download unable to find wget or curl"
+  return 1
+}
+github_http_copy() {
+  tmp=$(mktemp)
+  github_http_download "${tmp}" "$@" || return 1
+  body=$(cat "$tmp")
+  rm -f "${tmp}"
+  echo "$body"
+}
+github_release() {
+  owner_repo=$1
+  version=$2
+  test -z "$version" && version="latest"
+  giturl="https://github.com/${owner_repo}/releases/${version}"
+  json=$(github_http_copy "$giturl" "Accept:application/json")
+  test -z "$json" && return 1
+  version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
+  test -z "$version" && return 1
+  echo "$version"
+}
+
+# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
+EMBEDDED_CHECKSUMS=""
+
+# Find embedded checksum for a given version and filename
+find_embedded_checksum() {
+  version="$1"
+  filename="$2"
+  echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
+}
+parse_args() {
+  SEPARATOR_FOUND=0
+  TOOL_ARGS=""
+  while [ $# -gt 0 ]; do
+    case "$1" in
+    -d) log_set_priority 10 ;;
+    -h | --help | \?) usage "$0" ;;
+    -x) set -x ;;
+    --)
+      SEPARATOR_FOUND=1
+      shift
+      break
+      ;;
+    -*)
+      usage "$0"
+      ;;
+    *)
+      if [ $SEPARATOR_FOUND -eq 0 ]; then
+        # First non-flag argument is the tag/version
+        TAG="$1"
+      else
+        TOOL_ARGS="$1 $TOOL_ARGS"
+      fi
+      ;;
+    esac
+    shift
+  done
+
+  # Collect remaining arguments after --
+  while [ $# -gt 0 ]; do
+    TOOL_ARGS="$TOOL_ARGS $1"
+    shift
+  done
+  TAG="${TAG:-latest}"
+}
+tag_to_version() {
+  if [ "$TAG" = "latest" ]; then
+    log_info "checking GitHub for latest tag"
+    REALTAG=$(github_release "${REPO}" "${TAG}") && true
+    test -n "$REALTAG" || {
+      log_crit "Could not determine latest tag for ${REPO}"
+      exit 1
+    }
+  else
+    # Assume TAG is a valid tag/version string
+    REALTAG="$TAG"
+  fi
+  if test -z "$REALTAG"; then
+    log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
+    exit 1
+  fi
+  VERSION=${REALTAG#v} # Strip leading 'v'
+  TAG="$REALTAG"       # Use the resolved tag
+  log_info "Resolved version: ${VERSION} (tag: ${TAG})"
+}
+
+
+capitalize() {
+  input="$1"
+  first_char=$(printf "%s" "$input" | cut -c1)
+  first_upper=$(printf "%s" "$first_char" | tr '[:lower:]' '[:upper:]')
+  printf "%s%s\n" "$first_upper" "$(printf "%s" "$input" | cut -c2-)"
+}
+
+resolve_asset_filename() {
+
+  OS="$(capitalize "${OS}")"
+  # --- Apply Rules ---
+  ASSET_FILENAME=""
+  if [ "${UNAME_ARCH}" = 'amd64' ] && true
+  then
+    ARCH='x86_64'
+  fi
+  if [ "${UNAME_ARCH}" = '386' ] && true
+  then
+    ARCH='i386'
+  fi
+  if [ "${UNAME_OS}" = 'windows' ] && true
+  then
+    EXT='.zip'
+  fi
+  if [ -z "${ASSET_FILENAME}" ]; then
+    ASSET_FILENAME="${NAME}_${OS}_${ARCH}${EXT}"
+  fi
+}
+# Cleanup function to remove temporary files
+cleanup() {
+  if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
+    log_debug "Cleaning up temporary directory: $TMPDIR"
+    rm -rf -- "$TMPDIR"
+  fi
+}
+
+execute() {
+  STRIP_COMPONENTS=0
+  CHECKSUM_FILENAME="checksums.txt"
+
+  # --- Construct URLs ---
+  GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
+  ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
+  CHECKSUM_URL=""
+  if [ -n "$CHECKSUM_FILENAME" ]; then
+    CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
+  fi
+
+  # --- Download and Verify ---
+  TMPDIR=$(mktemp -d)
+  trap cleanup EXIT HUP INT TERM
+  log_debug "Downloading files into ${TMPDIR}"
+  log_info "Downloading ${ASSET_URL}"
+  github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
+
+  # Try to find embedded checksum first
+  EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
+
+  if [ -n "$EMBEDDED_HASH" ]; then
+    log_info "Using embedded checksum for verification"
+
+    # Verify using embedded hash
+    got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
+    if [ "$got" != "$EMBEDDED_HASH" ]; then
+      log_crit "Checksum verification failed for ${ASSET_FILENAME}"
+      log_crit "Expected: ${EMBEDDED_HASH}"
+      log_crit "Got: ${got}"
+      return 1
+    fi
+    log_info "Checksum verification successful"
+  elif [ -n "$CHECKSUM_URL" ]; then
+    # Fall back to downloading checksum file
+    log_info "Downloading checksums from ${CHECKSUM_URL}"
+    github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
+    log_info "Verifying checksum ..."
+    hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
+  else
+    log_info "No checksum found, skipping verification."
+  fi
+
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    log_debug "Target is raw binary"
+  else
+    log_info "Extracting ${ASSET_FILENAME}..."
+    (cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
+  fi
+  BINARY_NAME='binst'
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
+  else
+    BINARY_PATH="${TMPDIR}/binst"
+  fi
+
+  if [ "${UNAME_OS}" = "windows" ]; then
+    case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
+    case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
+  fi
+
+  if [ ! -f "${BINARY_PATH}" ]; then
+    log_crit "Binary not found: ${BINARY_PATH}"
+    log_crit "Listing contents of ${TMPDIR} ..."
+    if command -v find >/dev/null 2>&1; then
+      cd "${TMPDIR}" && find .
+    else
+      cd "${TMPDIR}" && ls -R .
+    fi
+    return 1
+  fi
+  # Make binary executable for runner script
+  chmod +x "${BINARY_PATH}"
+  # Run the binary directly with provided arguments
+  log_info "Running ${BINARY_NAME}${TOOL_ARGS:+ with arguments:$TOOL_ARGS}"
+  exec "${BINARY_PATH}" $TOOL_ARGS
+}
+
+# --- Configuration  ---
+NAME='binst'
+REPO='binary-install/binstaller'
+EXT='.tar.gz'
+
+# use in logging routines
+log_prefix() {
+  echo "${REPO}"
+}
+
+parse_args "$@"
+
+# --- Determine target platform ---
+OS="${BINSTALLER_OS:-$(uname_os)}"
+UNAME_OS="${OS}"
+
+ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
+UNAME_ARCH="${ARCH}"
+log_info "Detected Platform: ${OS}/${ARCH}"
+
+# --- Validate platform ---
+uname_os_check "$OS"
+uname_arch_check "$ARCH"
+
+tag_to_version
+
+resolve_asset_filename
+
+execute
diff --git a/scripts/gen.sh b/scripts/gen.sh
new file mode 100755
index 0000000..f0bad7c
--- /dev/null
+++ scripts/gen.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+BINST_VERSION="v0.7.6"
+./scripts/binst.sh "${BINST_VERSION}" -- gen --config .config/binstaller/reviewdog.yml -o install-reviewdog.sh
+./scripts/binst.sh "${BINST_VERSION}" -- gen --config .config/binstaller/reviewdog-nightly.yml -o install-nightly.sh

Description

This PR replaces the dynamic installation script downloading mechanism with pre-generated, committed installation scripts using binstaller. The main changes are:

  1. Adds YAML configuration files for both stable and nightly reviewdog releases
  2. Generates two new installation scripts (install-reviewdog.sh and install-nightly.sh) using the binstaller tool
  3. Modifies install.sh to use local scripts instead of downloading from remote URLs
  4. Updates GitHub Actions workflow dependencies
  5. Adds a helper script (binst.sh) to run the binstaller tool
  6. Includes a generation script (gen.sh) to regenerate the install scripts

The motivation is to improve reliability by using a maintained installer tool and to fix bugs in the previous godownloader-based script (which didn't properly handle .sbom.json files).

Possible Issues

  1. Generated Scripts Are Large: The two generated scripts are 550+ lines each and nearly identical. This increases repository size and maintenance burden.

  2. No CI Validation: There's no automated testing to ensure the generated scripts actually work or that they match the configuration files.

  3. Manual Regeneration: The generation process (scripts/gen.sh) must be run manually. There's no CI check to verify scripts are up-to-date with config changes.

  4. Hard-coded Version in gen.sh: The BINST_VERSION="v0.7.6" is hard-coded in scripts/gen.sh, making updates require code changes.

  5. Removed SHA Pinning: The old install scripts were pinned to specific commits (fd597144... and 30fccfe9...). The new approach downloads from releases without commit pinning.

Security Hotspots

  1. Local Script Execution Without Validation (High Risk - install.sh lines 25):

    cat "${INSTALL_SCRIPT}" | sh -s -- -b "${TEMP}/reviewdog/bin" "${VERSION}" 2>&1

    The script reads from $GITHUB_ACTION_PATH/install-reviewdog.sh without any integrity checks. If an attacker can modify files in the action's directory, they can execute arbitrary code. While GitHub Actions should protect this path, it's still a departure from the previous approach which downloaded from a pinned commit.

  2. Empty EMBEDDED_CHECKSUMS (Medium Risk - install-nightly.sh line 361 and install-reviewdog.sh line 360):

    EMBEDDED_CHECKSUMS=""

    The scripts support embedded checksums but none are configured. This means all downloads rely on fetching checksums.txt from GitHub releases. If an attacker compromises the release or performs a MitM attack, they could serve malicious binaries with matching fake checksums.

  3. GITHUB_TOKEN Authentication (Low Risk - install-nightly.sh lines 293-328):
    The scripts now support GITHUB_TOKEN for authentication, which could leak tokens in logs if debug logging is enabled and GitHub's masking fails. However, this is a common pattern and the logging uses log_debug.

Changes

Changes

Configuration Files

  • .config/binstaller/reviewdog.yml: Defines installation spec for stable reviewdog releases, including asset naming conventions, supported platforms (darwin/linux/windows with various architectures), and checksum configuration
  • .config/binstaller/reviewdog-nightly.yml: Similar configuration for nightly builds from reviewdog/nightly repo with slightly different asset naming rules

Installation Scripts

  • install-reviewdog.sh (new): 553-line generated installer for stable reviewdog releases with SHA256 checksum verification, platform detection, GitHub token support, and extraction logic
  • install-nightly.sh (new): 554-line generated installer for nightly builds, nearly identical to the stable version
  • install.sh (modified): Changed from downloading remote scripts via curl/wget to using local scripts from $GITHUB_ACTION_PATH, simplifying the code significantly

Helper Scripts

  • scripts/binst.sh (new): 574-line runner script that downloads and executes the binstaller tool directly without installation
  • scripts/gen.sh (new): Simple 4-line script to regenerate both installation scripts using binstaller v0.7.6

Documentation & Dependencies

  • README.md: Updated all examples to use pinned SHA e04ffabe3898a0af8d0fb1af00c188831c4b5893 instead of v1
  • .github/workflows/: Updated several action versions (depup, shellcheck, misspell, alex, actionlint)
sequenceDiagram
    participant User
    participant install.sh
    participant install-reviewdog.sh
    participant GitHub
    participant System

    User->>install.sh: Execute with VERSION
    install.sh->>install.sh: Set TEMP directory
    
    alt VERSION == 'nightly'
        install.sh->>install.sh: Set INSTALL_SCRIPT to install-nightly.sh
    else
        install.sh->>install.sh: Set INSTALL_SCRIPT to install-reviewdog.sh
    end
    
    install.sh->>install-reviewdog.sh: Execute via sh -s
    
    install-reviewdog.sh->>install-reviewdog.sh: Parse arguments
    install-reviewdog.sh->>install-reviewdog.sh: Detect platform (OS/ARCH)
    install-reviewdog.sh->>install-reviewdog.sh: Validate platform
    
    alt TAG == 'latest'
        install-reviewdog.sh->>GitHub: Request latest release info
        GitHub-->>install-reviewdog.sh: Return tag name
    end
    
    install-reviewdog.sh->>install-reviewdog.sh: Resolve asset filename
    install-reviewdog.sh->>GitHub: Download asset
    GitHub-->>install-reviewdog.sh: Binary archive
    
    install-reviewdog.sh->>GitHub: Download checksums.txt
    GitHub-->>install-reviewdog.sh: Checksum file
    
    install-reviewdog.sh->>install-reviewdog.sh: Verify SHA256 checksum
    install-reviewdog.sh->>install-reviewdog.sh: Extract archive
    install-reviewdog.sh->>System: Install binary to BINDIR
    install-reviewdog.sh->>System: Add to PATH
    
    install-reviewdog.sh-->>install.sh: Success
    install.sh-->>User: reviewdog installed
Loading

@renovate renovate bot changed the title chore(deps): update reviewdog/action-setup action to v1.4.0 Update reviewdog/action-setup action to v1.4.0 Nov 15, 2025
@renovate renovate bot changed the title Update reviewdog/action-setup action to v1.4.0 chore(deps): update reviewdog/action-setup action to v1.4.0 Nov 15, 2025
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from 49a0e32 to 81ca2f4 Compare November 25, 2025 15:02
@renovate renovate bot changed the title chore(deps): update reviewdog/action-setup action to v1.4.0 chore(deps): update reviewdog/action-setup action to v1.5.0 Nov 25, 2025
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from 81ca2f4 to af1dd24 Compare December 15, 2025 16:58
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from af1dd24 to 40c9dc2 Compare December 31, 2025 17:37
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from 40c9dc2 to fb1569b Compare February 2, 2026 15:32
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from fb1569b to 0eba8db Compare March 5, 2026 11:13
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from 0eba8db to 2747a3c Compare March 26, 2026 17:11
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch 2 times, most recently from 3faec0f to 63b6a79 Compare April 15, 2026 10:12
@renovate renovate bot force-pushed the renovate/reviewdog-action-setup-1-x branch from 63b6a79 to 90ae237 Compare April 16, 2026 16:01
@github-actions
Copy link
Copy Markdown
Contributor

[puLL-Merge] - reviewdog/action-setup@v1.3.2..v1.5.0

Diff
diff --git a/.config/binstaller/reviewdog-nightly.yml b/.config/binstaller/reviewdog-nightly.yml
new file mode 100644
index 0000000..3f4427d
--- /dev/null
+++ .config/binstaller/reviewdog-nightly.yml
@@ -0,0 +1,23 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
+schema: v1
+repo: reviewdog/nightly
+name: reviewdog
+asset:
+  template: reviewdog_${VERSION}_${OS}_${ARCH}${EXT}
+  default_extension: .tar.gz
+  rules:
+  - when:
+      arch: amd64
+    arch: x86_64
+  - when:
+      os: darwin
+    os: Darwin
+  - when:
+      os: linux
+    os: Linux
+  - when:
+      os: windows
+    os: Windows
+checksums:
+  algorithm: sha256
+  template: checksums.txt
diff --git a/.config/binstaller/reviewdog.yml b/.config/binstaller/reviewdog.yml
new file mode 100644
index 0000000..759b32e
--- /dev/null
+++ .config/binstaller/reviewdog.yml
@@ -0,0 +1,48 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/binary-install/binstaller/main/schema/InstallSpec.json
+# TODO: Generate install.sh from GitHub Actions and support gh attestation.
+# https://github.com/binary-install/binstaller is still WIP but it should be ok
+# since it's built and maintained by me (haya14busa, a maintainer of reviewdog).
+# The installer script works fine and fixed bugs which existed in the previous
+# script generated by godownloader. Example: The previous script didn't work if
+# release artifacts contains <binary-asset>.sbom.json since the prev script
+# just used grep with <binary-asset> without '$'.
+schema: v1
+name: reviewdog
+repo: reviewdog/reviewdog
+asset:
+  template: ${NAME}_${VERSION}_${OS}_${ARCH}${EXT}
+  default_extension: .tar.gz
+  rules:
+  - when:
+      arch: amd64
+    arch: x86_64
+  - when:
+      arch: "386"
+    arch: i386
+  naming_convention:
+    os: titlecase
+    arch: lowercase
+checksums:
+  algorithm: sha256
+  template: checksums.txt
+supported_platforms:
+- os: darwin
+  arch: amd64
+- os: darwin
+  arch: arm64
+- os: linux
+  arch: "386"
+- os: linux
+  arch: amd64
+- os: linux
+  arch: arm64
+- os: linux
+  arch: armv6
+- os: windows
+  arch: "386"
+- os: windows
+  arch: amd64
+- os: windows
+  arch: arm64
+- os: windows
+  arch: armv6
diff --git .github/workflows/depup.yml .github/workflows/depup.yml
index 95cd2f1..ba4b920 100644
--- .github/workflows/depup.yml
+++ .github/workflows/depup.yml
@@ -9,15 +9,24 @@ on:
 jobs:
   reviewdog:
     runs-on: ubuntu-latest
+    permissions:
+      id-token: write # Enable OIDC
+      pull-requests: write
+      contents: write
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
-      - uses: haya14busa/action-depup@680d0a6dedb9c170ec21b8e1eacf7a1133937743 # v1.6.2
+      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
+      - uses: haya14busa/action-depup@94a1aaf4e4923064019214b48a43276218af7ad5 # v1.6.4
         id: depup
         with:
           file: README.md
           version_name: reviewdog_version
           repo: reviewdog/reviewdog
 
+      # Configure signed commits
+      # https://www.chainguard.dev/unchained/keyless-git-commit-signing-with-gitsign-and-github-actions
+      # https://github.com/chainguard-dev/actions/commits/main/setup-gitsign
+      - uses: chainguard-dev/actions/setup-gitsign@4aa34024d63633a127b08bf0446655e8e4b27b52 # main branch as of 2024-12-27
+
       - name: Create Pull Request
         uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
         with:
diff --git .github/workflows/release.yml .github/workflows/release.yml
index a344bb1..a0e962f 100644
--- .github/workflows/release.yml
+++ .github/workflows/release.yml
@@ -14,17 +14,17 @@ jobs:
     if: github.event.action != 'labeled'
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
 
       # Bump version on merging Pull Requests with specific labels.
       # (bump:major,bump:minor,bump:patch)
       - id: bumpr
         if: "!startsWith(github.ref, 'refs/tags/')"
-        uses: haya14busa/action-bumpr@78ab5a104d20896c9c9122c64221b3aecf1a8cbb # v1.10.0
+        uses: haya14busa/action-bumpr@faf6f474bcb6174125cfc569f0b2e24cbf03d496 # v1.11.4
 
       # Update corresponding major and minor tag.
       # e.g. Update v1 and v1.2 when releasing v1.2.3
-      - uses: haya14busa/action-update-semver@fb48464b2438ae82cc78237be61afb4f461265a1 # v1.2.1
+      - uses: haya14busa/action-update-semver@7d2c558640ea49e798d46539536190aff8c18715 # v1.5.1
         if: "!steps.bumpr.outputs.skip"
         with:
           tag: ${{ steps.bumpr.outputs.next_version }}
@@ -50,6 +50,6 @@ jobs:
     if: github.event.action == 'labeled'
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
       - name: Post bumpr status comment
-        uses: haya14busa/action-bumpr@78ab5a104d20896c9c9122c64221b3aecf1a8cbb # v1.10.0
+        uses: haya14busa/action-bumpr@faf6f474bcb6174125cfc569f0b2e24cbf03d496 # v1.11.4
diff --git .github/workflows/reviewdog.yml .github/workflows/reviewdog.yml
index 95b880e..a695252 100644
--- .github/workflows/reviewdog.yml
+++ .github/workflows/reviewdog.yml
@@ -9,14 +9,14 @@ jobs:
     name: runner / shellcheck
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
       - uses: haya14busa/action-cond@94f77f7a80cd666cb3155084e428254fea4281fd # v1.2.1
         id: reporter
         with:
           cond: ${{ github.event_name == 'pull_request' }}
           if_true: "github-pr-review"
           if_false: "github-check"
-      - uses: reviewdog/action-shellcheck@6e0e63d1750d02d761b3df0f2c5ba9f9ac4a9ed7 # v1.29.0
+      - uses: reviewdog/action-shellcheck@4c07458293ac342d477251099501a718ae5ef86e # v1.32.0
         with:
           github_token: ${{ secrets.github_token }}
           reporter: ${{ steps.reporter.outputs.value }}
@@ -26,7 +26,7 @@ jobs:
     name: runner / suggester / shell
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
       - name: install mvdan/sh
         run: |
           shfmtversion="v3.6.0"
@@ -56,8 +56,8 @@ jobs:
     name: runner / misspell
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
-      - uses: reviewdog/action-misspell@18ffb61effb93b47e332f185216be7e49592e7e1 # v1.26.1
+      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
+      - uses: reviewdog/action-misspell@d6429416b12b09b4e2768307d53bef58d172e962 # v1.27.0
         with:
           github_token: ${{ secrets.github_token }}
           reporter: github-check
@@ -68,8 +68,8 @@ jobs:
     name: runner / alex
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
-      - uses: reviewdog/action-alex@986cf7dd82e702f82b4173deaa793a849f5b719d # v1.15.0
+      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
+      - uses: reviewdog/action-alex@6083b8ca333981fa617c6828c5d8fb21b13d916b # v1.16.0
         with:
           github_token: ${{ secrets.github_token }}
           reporter: github-check
@@ -79,8 +79,8 @@ jobs:
     name: runner / actionlint
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
-      - uses: reviewdog/action-actionlint@db58217885f9a6570da9c71be4e40ec33fe44a1f # v1.65.0
+      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
+      - uses: reviewdog/action-actionlint@f00ad0691526c10be4021a91b2510f0a769b14d0 # v1.68.0
         with:
           github_token: ${{ secrets.github_token }}
           reporter: github-check
diff --git .github/workflows/test.yml .github/workflows/test.yml
index 53d1aa4..ec731ae 100644
--- .github/workflows/test.yml
+++ .github/workflows/test.yml
@@ -11,7 +11,7 @@ jobs:
         platform: [ubuntu-latest, macos-latest, windows-latest]
     runs-on: ${{ matrix.platform }}
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
 
       - name: install reviewdog (default)
         uses: ./
@@ -34,7 +34,7 @@ jobs:
     container:
       image: alpine:latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1
       - name: install reviewdog
         uses: ./
       - run: reviewdog -version
diff --git README.md README.md
index d49e902..e21d885 100644
--- README.md
+++ README.md
@@ -23,23 +23,23 @@ inputs:
 ### Latest
 ```yaml
 steps:
-  - uses: reviewdog/action-setup@v1
+  - uses: reviewdog/action-setup@d8edfce3dd5e1ec6978745e801f9c50b5ef80252 # v1.4.0
   - run: reviewdog -version
 ```
 
 ### Specify reviewdog version
 ```yaml
 steps:
-  - uses: reviewdog/action-setup@v1
+  - uses: reviewdog/action-setup@d8edfce3dd5e1ec6978745e801f9c50b5ef80252 # v1.4.0
     with:
-      reviewdog_version: v0.20.3
+      reviewdog_version: v0.21.0
   - run: reviewdog -version
 ```
 
 ### Nightly
 ```yaml
 steps:
-  - uses: reviewdog/action-setup@v1
+  - uses: reviewdog/action-setup@d8edfce3dd5e1ec6978745e801f9c50b5ef80252 # v1.4.0
     with:
       reviewdog_version: nightly
   - run: reviewdog -version
diff --git a/install-nightly.sh b/install-nightly.sh
new file mode 100755
index 0000000..f0233f4
--- /dev/null
+++ install-nightly.sh
@@ -0,0 +1,554 @@
+#!/bin/sh
+# Code generated by binstaller. DO NOT EDIT.
+#
+set -e
+usage() {
+  this=$1
+  cat <<EOF
+$this: download ${NAME} from ${REPO}
+
+Usage: $this [-b bindir] [-d] [-n] [tag]
+  -b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
+  -d turns on debug logging
+  -n turns on dry run mode
+   [tag] is a tag from
+   https://github.com/reviewdog/nightly/releases
+   If tag is missing, then the latest will be used.
+
+ Generated by binstaller
+  https://github.com/binary-install/binstaller
+EOF
+  exit 2
+}
+
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+https://github.com/client9/shlib - portable posix shell functions
+Public domain - http://unlicense.org
+https://github.com/client9/shlib/blob/master/LICENSE.md
+but credit (and pull requests) appreciated.
+------------------------------------------------------------------------
+EOF
+is_command() {
+  command -v "$1" >/dev/null
+}
+echoerr() {
+  echo "$@" 1>&2
+}
+_logp=6
+log_set_priority() {
+  _logp="$1"
+}
+log_priority() {
+  if test -z "$1"; then
+    echo "$_logp"
+    return
+  fi
+  [ "$1" -le "$_logp" ]
+}
+log_tag() {
+  case $1 in
+    0) echo "emerg" ;;
+    1) echo "alert" ;;
+    2) echo "crit" ;;
+    3) echo "err" ;;
+    4) echo "warning" ;;
+    5) echo "notice" ;;
+    6) echo "info" ;;
+    7) echo "debug" ;;
+    *) echo "$1" ;;
+  esac
+}
+log_debug() {
+  log_priority 7 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
+}
+log_info() {
+  log_priority 6 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
+}
+log_err() {
+  log_priority 3 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
+}
+log_crit() {
+  log_priority 2 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
+}
+uname_os() {
+  os=$(uname -s | tr '[:upper:]' '[:lower:]')
+  case "$os" in
+    msys*) os="windows" ;;
+    mingw*) os="windows" ;;
+    cygwin*) os="windows" ;;
+  esac
+  if [ "$os" = "sunos" ]; then
+    if [ "$(uname -o)" = "illumos" ]; then
+      os="illumos"
+    else
+      os="solaris"
+    fi
+  fi
+  echo "$os"
+}
+uname_arch() {
+  arch=$(uname -m)
+  case $arch in
+    x86_64) arch="amd64" ;;
+    i86pc) arch="amd64" ;;
+    x86) arch="386" ;;
+    i686) arch="386" ;;
+    i386) arch="386" ;;
+    aarch64) arch="arm64" ;;
+    armv5*) arch="armv5" ;;
+    armv6*) arch="armv6" ;;
+    armv7*) arch="armv7" ;;
+  esac
+  echo "${arch}"
+}
+uname_os_check() {
+  os=$(uname_os)
+  case "$os" in
+    darwin) return 0 ;;
+    dragonfly) return 0 ;;
+    freebsd) return 0 ;;
+    linux) return 0 ;;
+    android) return 0 ;;
+    midnightbsd) return 0 ;;
+    nacl) return 0 ;;
+    netbsd) return 0 ;;
+    openbsd) return 0 ;;
+    plan9) return 0 ;;
+    solaris) return 0 ;;
+    illumos) return 0 ;;
+    windows) return 0 ;;
+  esac
+  log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
+  return 1
+}
+uname_arch_check() {
+  arch=$(uname_arch)
+  case "$arch" in
+    386) return 0 ;;
+    amd64) return 0 ;;
+    arm64) return 0 ;;
+    armv5) return 0 ;;
+    armv6) return 0 ;;
+    armv7) return 0 ;;
+    ppc64) return 0 ;;
+    ppc64le) return 0 ;;
+    mips) return 0 ;;
+    mipsle) return 0 ;;
+    mips64) return 0 ;;
+    mips64le) return 0 ;;
+    s390x) return 0 ;;
+    amd64p32) return 0 ;;
+  esac
+  log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib"
+  return 1
+}
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+End of functions from https://github.com/client9/shlib
+------------------------------------------------------------------------
+EOF
+
+
+hash_sha256() {
+  TARGET=${1:-/dev/stdin}
+  if is_command gsha256sum; then
+    hash=$(gsha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command sha256sum; then
+    hash=$(sha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command shasum; then
+    hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command openssl; then
+    hash=$(openssl dgst -sha256 "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 2
+  else
+    log_crit "hash_sha256 unable to find command to compute sha-256 hash"
+    return 1
+  fi
+}
+
+hash_compute() {
+  hash_sha256 "$1"
+}
+
+
+untar() {
+  tarball=$1
+  strip_components=${2:-0} # default 0
+  case "${tarball}" in
+  *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
+  *.gz) gunzip "${tarball}" ;;
+  *.zip)
+    # unzip doesn't have a standard --strip-components
+    # Workaround: extract to a subdir and move contents up if stripping
+    if [ "$strip_components" -gt 0 ]; then
+      extract_dir=$(basename "${tarball%.zip}")_extracted
+      unzip -q "${tarball}" -d "${extract_dir}"
+      # Move contents of the *first* directory found inside extract_dir up
+      # This assumes wrap_in_directory=true convention
+      first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
+      if [ -n "$first_subdir" ]; then
+        # Move all contents (* includes hidden files)
+        mv "${first_subdir}"/* .
+        # Optionally remove the now-empty subdir and the extract_dir
+        rmdir "${first_subdir}"
+        rmdir "${extract_dir}"
+      else
+        log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
+        # Files are extracted in current dir anyway, proceed
+      fi
+    else
+      unzip -q "${tarball}"
+    fi
+    ;;
+  *)
+    log_err "untar unknown archive format for ${tarball}"
+    return 1
+    ;;
+  esac
+}
+
+
+
+hash_verify() {
+  TARGET_PATH=$1
+  SUMFILE=$2
+  if [ -z "${SUMFILE}" ]; then
+    log_err "hash_verify checksum file not specified in arg2"
+    return 1
+  fi
+  got=$(hash_compute "$TARGET_PATH")
+  if [ -z "${got}" ]; then
+    log_err "failed to calculate hash: ${TARGET_PATH}"
+    return 1
+  fi
+
+  BASENAME=${TARGET_PATH##*/}
+
+  # Check for line matches in checksum file
+  # Format: "<hash>  <filename>" or "<hash> *<filename>"
+  # Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
+  while IFS= read -r line || [ -n "$line" ]; do
+    # Normalize tabs to spaces
+    line=$(echo "$line" | tr '\t' ' ')
+
+    # Remove trailing spaces for hash-only line check
+    line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
+
+    # Check for hash-only line (no filename) - early return
+    if [ "$line_trimmed" = "$got" ]; then
+      return 0
+    fi
+
+    # Extract hash and filename parts
+    # First field is the hash, rest is filename (which may contain spaces)
+    line_hash=$(echo "$line" | cut -d' ' -f1)
+
+    # Skip if hash doesn't match
+    if [ "$line_hash" != "$got" ]; then
+      continue
+    fi
+
+    # Hash matches, now check filename
+    # Remove the hash part from the beginning of the line
+    line_rest="${line#"$got"}"
+    # Remove leading spaces
+    while [ "${line_rest#[ ]}" != "$line_rest" ]; do
+      line_rest="${line_rest#[ ]}"
+    done
+
+    # Remove leading asterisk if present (binary mode indicator)
+    if [ "${line_rest#\*}" != "$line_rest" ]; then
+      line_rest="${line_rest#\*}"
+    fi
+
+    # Extract just the filename without any path
+    line_filename="${line_rest##*/}"
+
+    # Check if the filename matches
+    if [ "$line_filename" = "$BASENAME" ]; then
+      return 0
+    fi
+  done < "$SUMFILE"
+
+  log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
+  log_err "  Expected hash: ${got}"
+  log_err "  Checksum file content:"
+  cat "$SUMFILE" >&2
+  return 1
+}
+
+# GitHub HTTP download functions with GITHUB_TOKEN support
+github_http_download_curl() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      curl -fsSL -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "$header" -o "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download_wget() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
+    else
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      wget -q -O "$local_file" "$source_url"
+    else
+      wget -q --header "$header" -O "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download() {
+  log_debug "github_http_download $2"
+  if is_command curl; then
+    github_http_download_curl "$@"
+    return
+  elif is_command wget; then
+    github_http_download_wget "$@"
+    return
+  fi
+  log_crit "github_http_download unable to find wget or curl"
+  return 1
+}
+github_http_copy() {
+  tmp=$(mktemp)
+  github_http_download "${tmp}" "$@" || return 1
+  body=$(cat "$tmp")
+  rm -f "${tmp}"
+  echo "$body"
+}
+github_release() {
+  owner_repo=$1
+  version=$2
+  test -z "$version" && version="latest"
+  giturl="https://github.com/${owner_repo}/releases/${version}"
+  json=$(github_http_copy "$giturl" "Accept:application/json")
+  test -z "$json" && return 1
+  version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
+  test -z "$version" && return 1
+  echo "$version"
+}
+
+# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
+EMBEDDED_CHECKSUMS=""
+
+# Find embedded checksum for a given version and filename
+find_embedded_checksum() {
+  version="$1"
+  filename="$2"
+  echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
+}
+parse_args() {
+  BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
+  DRY_RUN=0
+  while getopts "b:dqh?xn" arg; do
+    case "$arg" in
+    b) BINDIR="$OPTARG" ;;
+    d) log_set_priority 10 ;;
+    q) log_set_priority 3 ;;
+    h | \?) usage "$0" ;;
+    x) set -x ;;
+    n) DRY_RUN=1 ;;
+    esac
+  done
+  shift $((OPTIND - 1))
+  TAG="${1:-latest}"
+}
+tag_to_version() {
+  if [ "$TAG" = "latest" ]; then
+    log_info "checking GitHub for latest tag"
+    REALTAG=$(github_release "${REPO}" "${TAG}") && true
+    test -n "$REALTAG" || {
+      log_crit "Could not determine latest tag for ${REPO}"
+      exit 1
+    }
+  else
+    # Assume TAG is a valid tag/version string
+    REALTAG="$TAG"
+  fi
+  if test -z "$REALTAG"; then
+    log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
+    exit 1
+  fi
+  VERSION=${REALTAG#v} # Strip leading 'v'
+  TAG="$REALTAG"       # Use the resolved tag
+  log_info "Resolved version: ${VERSION} (tag: ${TAG})"
+}
+
+
+
+resolve_asset_filename() {
+
+  # --- Apply Rules ---
+  ASSET_FILENAME=""
+  if [ "${UNAME_ARCH}" = 'amd64' ] && true
+  then
+    ARCH='x86_64'
+  fi
+  if [ "${UNAME_OS}" = 'darwin' ] && true
+  then
+    OS='Darwin'
+  fi
+  if [ "${UNAME_OS}" = 'linux' ] && true
+  then
+    OS='Linux'
+  fi
+  if [ "${UNAME_OS}" = 'windows' ] && true
+  then
+    OS='Windows'
+  fi
+  if [ -z "${ASSET_FILENAME}" ]; then
+    ASSET_FILENAME="reviewdog_${VERSION}_${OS}_${ARCH}${EXT}"
+  fi
+}
+# Cleanup function to remove temporary files
+cleanup() {
+  if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
+    log_debug "Cleaning up temporary directory: $TMPDIR"
+    rm -rf -- "$TMPDIR"
+  fi
+}
+
+execute() {
+  STRIP_COMPONENTS=0
+  CHECKSUM_FILENAME="checksums.txt"
+
+  # --- Construct URLs ---
+  GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
+  ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
+  CHECKSUM_URL=""
+  if [ -n "$CHECKSUM_FILENAME" ]; then
+    CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
+  fi
+
+  # --- Download and Verify ---
+  TMPDIR=$(mktemp -d)
+  trap 'rm -rf -- "$TMPDIR"' EXIT HUP INT TERM
+  log_debug "Downloading files into ${TMPDIR}"
+  log_info "Downloading ${ASSET_URL}"
+  github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
+
+  # Try to find embedded checksum first
+  EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
+
+  if [ -n "$EMBEDDED_HASH" ]; then
+    log_info "Using embedded checksum for verification"
+
+    # Verify using embedded hash
+    got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
+    if [ "$got" != "$EMBEDDED_HASH" ]; then
+      log_crit "Checksum verification failed for ${ASSET_FILENAME}"
+      log_crit "Expected: ${EMBEDDED_HASH}"
+      log_crit "Got: ${got}"
+      return 1
+    fi
+    log_info "Checksum verification successful"
+  elif [ -n "$CHECKSUM_URL" ]; then
+    # Fall back to downloading checksum file
+    log_info "Downloading checksums from ${CHECKSUM_URL}"
+    github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
+    log_info "Verifying checksum ..."
+    hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
+  else
+    log_info "No checksum found, skipping verification."
+  fi
+
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    log_debug "Target is raw binary"
+  else
+    log_info "Extracting ${ASSET_FILENAME}..."
+    (cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
+  fi
+  BINARY_NAME='reviewdog'
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
+  else
+    BINARY_PATH="${TMPDIR}/reviewdog"
+  fi
+
+  if [ "${UNAME_OS}" = "windows" ]; then
+    case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
+    case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
+  fi
+
+  if [ ! -f "${BINARY_PATH}" ]; then
+    log_crit "Binary not found: ${BINARY_PATH}"
+    log_crit "Listing contents of ${TMPDIR} ..."
+    if command -v find >/dev/null 2>&1; then
+      cd "${TMPDIR}" && find .
+    else
+      cd "${TMPDIR}" && ls -R .
+    fi
+    return 1
+  fi
+  # Install the binary
+  INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
+
+  if [ "$DRY_RUN" = "1" ]; then
+    log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
+  else
+    log_info "Installing binary to ${INSTALL_PATH}"
+    test ! -d "${BINDIR}" && install -d "${BINDIR}"
+    install "${BINARY_PATH}" "${INSTALL_PATH}"
+    log_info "${BINARY_NAME} installation complete!"
+  fi
+}
+
+# --- Configuration  ---
+NAME='reviewdog'
+REPO='reviewdog/nightly'
+EXT='.tar.gz'
+
+# use in logging routines
+log_prefix() {
+  echo "${REPO}"
+}
+
+parse_args "$@"
+
+# --- Determine target platform ---
+OS="${BINSTALLER_OS:-$(uname_os)}"
+UNAME_OS="${OS}"
+
+ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
+UNAME_ARCH="${ARCH}"
+log_info "Detected Platform: ${OS}/${ARCH}"
+
+# --- Validate platform ---
+uname_os_check "$OS"
+uname_arch_check "$ARCH"
+
+tag_to_version
+
+resolve_asset_filename
+
+execute
diff --git a/install-reviewdog.sh b/install-reviewdog.sh
new file mode 100755
index 0000000..e8ce78e
--- /dev/null
+++ install-reviewdog.sh
@@ -0,0 +1,553 @@
+#!/bin/sh
+# Code generated by binstaller. DO NOT EDIT.
+#
+set -e
+usage() {
+  this=$1
+  cat <<EOF
+$this: download ${NAME} from ${REPO}
+
+Usage: $this [-b bindir] [-d] [-n] [tag]
+  -b sets bindir or installation directory, Defaults to ${BINSTALLER_BIN:-${HOME}/.local/bin}
+  -d turns on debug logging
+  -n turns on dry run mode
+   [tag] is a tag from
+   https://github.com/reviewdog/reviewdog/releases
+   If tag is missing, then the latest will be used.
+
+ Generated by binstaller
+  https://github.com/binary-install/binstaller
+EOF
+  exit 2
+}
+
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+https://github.com/client9/shlib - portable posix shell functions
+Public domain - http://unlicense.org
+https://github.com/client9/shlib/blob/master/LICENSE.md
+but credit (and pull requests) appreciated.
+------------------------------------------------------------------------
+EOF
+is_command() {
+  command -v "$1" >/dev/null
+}
+echoerr() {
+  echo "$@" 1>&2
+}
+_logp=6
+log_set_priority() {
+  _logp="$1"
+}
+log_priority() {
+  if test -z "$1"; then
+    echo "$_logp"
+    return
+  fi
+  [ "$1" -le "$_logp" ]
+}
+log_tag() {
+  case $1 in
+    0) echo "emerg" ;;
+    1) echo "alert" ;;
+    2) echo "crit" ;;
+    3) echo "err" ;;
+    4) echo "warning" ;;
+    5) echo "notice" ;;
+    6) echo "info" ;;
+    7) echo "debug" ;;
+    *) echo "$1" ;;
+  esac
+}
+log_debug() {
+  log_priority 7 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
+}
+log_info() {
+  log_priority 6 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
+}
+log_err() {
+  log_priority 3 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
+}
+log_crit() {
+  log_priority 2 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
+}
+uname_os() {
+  os=$(uname -s | tr '[:upper:]' '[:lower:]')
+  case "$os" in
+    msys*) os="windows" ;;
+    mingw*) os="windows" ;;
+    cygwin*) os="windows" ;;
+  esac
+  if [ "$os" = "sunos" ]; then
+    if [ "$(uname -o)" = "illumos" ]; then
+      os="illumos"
+    else
+      os="solaris"
+    fi
+  fi
+  echo "$os"
+}
+uname_arch() {
+  arch=$(uname -m)
+  case $arch in
+    x86_64) arch="amd64" ;;
+    i86pc) arch="amd64" ;;
+    x86) arch="386" ;;
+    i686) arch="386" ;;
+    i386) arch="386" ;;
+    aarch64) arch="arm64" ;;
+    armv5*) arch="armv5" ;;
+    armv6*) arch="armv6" ;;
+    armv7*) arch="armv7" ;;
+  esac
+  echo "${arch}"
+}
+uname_os_check() {
+  os=$(uname_os)
+  case "$os" in
+    darwin) return 0 ;;
+    dragonfly) return 0 ;;
+    freebsd) return 0 ;;
+    linux) return 0 ;;
+    android) return 0 ;;
+    midnightbsd) return 0 ;;
+    nacl) return 0 ;;
+    netbsd) return 0 ;;
+    openbsd) return 0 ;;
+    plan9) return 0 ;;
+    solaris) return 0 ;;
+    illumos) return 0 ;;
+    windows) return 0 ;;
+  esac
+  log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
+  return 1
+}
+uname_arch_check() {
+  arch=$(uname_arch)
+  case "$arch" in
+    386) return 0 ;;
+    amd64) return 0 ;;
+    arm64) return 0 ;;
+    armv5) return 0 ;;
+    armv6) return 0 ;;
+    armv7) return 0 ;;
+    ppc64) return 0 ;;
+    ppc64le) return 0 ;;
+    mips) return 0 ;;
+    mipsle) return 0 ;;
+    mips64) return 0 ;;
+    mips64le) return 0 ;;
+    s390x) return 0 ;;
+    amd64p32) return 0 ;;
+  esac
+  log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib"
+  return 1
+}
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+End of functions from https://github.com/client9/shlib
+------------------------------------------------------------------------
+EOF
+
+
+hash_sha256() {
+  TARGET=${1:-/dev/stdin}
+  if is_command gsha256sum; then
+    hash=$(gsha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command sha256sum; then
+    hash=$(sha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command shasum; then
+    hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command openssl; then
+    hash=$(openssl dgst -sha256 "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 2
+  else
+    log_crit "hash_sha256 unable to find command to compute sha-256 hash"
+    return 1
+  fi
+}
+
+hash_compute() {
+  hash_sha256 "$1"
+}
+
+
+untar() {
+  tarball=$1
+  strip_components=${2:-0} # default 0
+  case "${tarball}" in
+  *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
+  *.gz) gunzip "${tarball}" ;;
+  *.zip)
+    # unzip doesn't have a standard --strip-components
+    # Workaround: extract to a subdir and move contents up if stripping
+    if [ "$strip_components" -gt 0 ]; then
+      extract_dir=$(basename "${tarball%.zip}")_extracted
+      unzip -q "${tarball}" -d "${extract_dir}"
+      # Move contents of the *first* directory found inside extract_dir up
+      # This assumes wrap_in_directory=true convention
+      first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
+      if [ -n "$first_subdir" ]; then
+        # Move all contents (* includes hidden files)
+        mv "${first_subdir}"/* .
+        # Optionally remove the now-empty subdir and the extract_dir
+        rmdir "${first_subdir}"
+        rmdir "${extract_dir}"
+      else
+        log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
+        # Files are extracted in current dir anyway, proceed
+      fi
+    else
+      unzip -q "${tarball}"
+    fi
+    ;;
+  *)
+    log_err "untar unknown archive format for ${tarball}"
+    return 1
+    ;;
+  esac
+}
+
+
+
+hash_verify() {
+  TARGET_PATH=$1
+  SUMFILE=$2
+  if [ -z "${SUMFILE}" ]; then
+    log_err "hash_verify checksum file not specified in arg2"
+    return 1
+  fi
+  got=$(hash_compute "$TARGET_PATH")
+  if [ -z "${got}" ]; then
+    log_err "failed to calculate hash: ${TARGET_PATH}"
+    return 1
+  fi
+
+  BASENAME=${TARGET_PATH##*/}
+
+  # Check for line matches in checksum file
+  # Format: "<hash>  <filename>" or "<hash> *<filename>"
+  # Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
+  while IFS= read -r line || [ -n "$line" ]; do
+    # Normalize tabs to spaces
+    line=$(echo "$line" | tr '\t' ' ')
+
+    # Remove trailing spaces for hash-only line check
+    line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
+
+    # Check for hash-only line (no filename) - early return
+    if [ "$line_trimmed" = "$got" ]; then
+      return 0
+    fi
+
+    # Extract hash and filename parts
+    # First field is the hash, rest is filename (which may contain spaces)
+    line_hash=$(echo "$line" | cut -d' ' -f1)
+
+    # Skip if hash doesn't match
+    if [ "$line_hash" != "$got" ]; then
+      continue
+    fi
+
+    # Hash matches, now check filename
+    # Remove the hash part from the beginning of the line
+    line_rest="${line#"$got"}"
+    # Remove leading spaces
+    while [ "${line_rest#[ ]}" != "$line_rest" ]; do
+      line_rest="${line_rest#[ ]}"
+    done
+
+    # Remove leading asterisk if present (binary mode indicator)
+    if [ "${line_rest#\*}" != "$line_rest" ]; then
+      line_rest="${line_rest#\*}"
+    fi
+
+    # Extract just the filename without any path
+    line_filename="${line_rest##*/}"
+
+    # Check if the filename matches
+    if [ "$line_filename" = "$BASENAME" ]; then
+      return 0
+    fi
+  done < "$SUMFILE"
+
+  log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
+  log_err "  Expected hash: ${got}"
+  log_err "  Checksum file content:"
+  cat "$SUMFILE" >&2
+  return 1
+}
+
+# GitHub HTTP download functions with GITHUB_TOKEN support
+github_http_download_curl() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      curl -fsSL -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "$header" -o "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download_wget() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
+    else
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      wget -q -O "$local_file" "$source_url"
+    else
+      wget -q --header "$header" -O "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download() {
+  log_debug "github_http_download $2"
+  if is_command curl; then
+    github_http_download_curl "$@"
+    return
+  elif is_command wget; then
+    github_http_download_wget "$@"
+    return
+  fi
+  log_crit "github_http_download unable to find wget or curl"
+  return 1
+}
+github_http_copy() {
+  tmp=$(mktemp)
+  github_http_download "${tmp}" "$@" || return 1
+  body=$(cat "$tmp")
+  rm -f "${tmp}"
+  echo "$body"
+}
+github_release() {
+  owner_repo=$1
+  version=$2
+  test -z "$version" && version="latest"
+  giturl="https://github.com/${owner_repo}/releases/${version}"
+  json=$(github_http_copy "$giturl" "Accept:application/json")
+  test -z "$json" && return 1
+  version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
+  test -z "$version" && return 1
+  echo "$version"
+}
+
+# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
+EMBEDDED_CHECKSUMS=""
+
+# Find embedded checksum for a given version and filename
+find_embedded_checksum() {
+  version="$1"
+  filename="$2"
+  echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
+}
+parse_args() {
+  BINDIR="${BINSTALLER_BIN:-${HOME}/.local/bin}"
+  DRY_RUN=0
+  while getopts "b:dqh?xn" arg; do
+    case "$arg" in
+    b) BINDIR="$OPTARG" ;;
+    d) log_set_priority 10 ;;
+    q) log_set_priority 3 ;;
+    h | \?) usage "$0" ;;
+    x) set -x ;;
+    n) DRY_RUN=1 ;;
+    esac
+  done
+  shift $((OPTIND - 1))
+  TAG="${1:-latest}"
+}
+tag_to_version() {
+  if [ "$TAG" = "latest" ]; then
+    log_info "checking GitHub for latest tag"
+    REALTAG=$(github_release "${REPO}" "${TAG}") && true
+    test -n "$REALTAG" || {
+      log_crit "Could not determine latest tag for ${REPO}"
+      exit 1
+    }
+  else
+    # Assume TAG is a valid tag/version string
+    REALTAG="$TAG"
+  fi
+  if test -z "$REALTAG"; then
+    log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
+    exit 1
+  fi
+  VERSION=${REALTAG#v} # Strip leading 'v'
+  TAG="$REALTAG"       # Use the resolved tag
+  log_info "Resolved version: ${VERSION} (tag: ${TAG})"
+}
+
+
+capitalize() {
+  input="$1"
+  first_char=$(printf "%s" "$input" | cut -c1)
+  first_upper=$(printf "%s" "$first_char" | tr '[:lower:]' '[:upper:]')
+  printf "%s%s\n" "$first_upper" "$(printf "%s" "$input" | cut -c2-)"
+}
+
+resolve_asset_filename() {
+
+  OS="$(capitalize "${OS}")"
+  # --- Apply Rules ---
+  ASSET_FILENAME=""
+  if [ "${UNAME_ARCH}" = 'amd64' ] && true
+  then
+    ARCH='x86_64'
+  fi
+  if [ "${UNAME_ARCH}" = '386' ] && true
+  then
+    ARCH='i386'
+  fi
+  if [ -z "${ASSET_FILENAME}" ]; then
+    ASSET_FILENAME="${NAME}_${VERSION}_${OS}_${ARCH}${EXT}"
+  fi
+}
+# Cleanup function to remove temporary files
+cleanup() {
+  if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
+    log_debug "Cleaning up temporary directory: $TMPDIR"
+    rm -rf -- "$TMPDIR"
+  fi
+}
+
+execute() {
+  STRIP_COMPONENTS=0
+  CHECKSUM_FILENAME="checksums.txt"
+
+  # --- Construct URLs ---
+  GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
+  ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
+  CHECKSUM_URL=""
+  if [ -n "$CHECKSUM_FILENAME" ]; then
+    CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
+  fi
+
+  # --- Download and Verify ---
+  TMPDIR=$(mktemp -d)
+  trap 'rm -rf -- "$TMPDIR"' EXIT HUP INT TERM
+  log_debug "Downloading files into ${TMPDIR}"
+  log_info "Downloading ${ASSET_URL}"
+  github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
+
+  # Try to find embedded checksum first
+  EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
+
+  if [ -n "$EMBEDDED_HASH" ]; then
+    log_info "Using embedded checksum for verification"
+
+    # Verify using embedded hash
+    got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
+    if [ "$got" != "$EMBEDDED_HASH" ]; then
+      log_crit "Checksum verification failed for ${ASSET_FILENAME}"
+      log_crit "Expected: ${EMBEDDED_HASH}"
+      log_crit "Got: ${got}"
+      return 1
+    fi
+    log_info "Checksum verification successful"
+  elif [ -n "$CHECKSUM_URL" ]; then
+    # Fall back to downloading checksum file
+    log_info "Downloading checksums from ${CHECKSUM_URL}"
+    github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
+    log_info "Verifying checksum ..."
+    hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
+  else
+    log_info "No checksum found, skipping verification."
+  fi
+
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    log_debug "Target is raw binary"
+  else
+    log_info "Extracting ${ASSET_FILENAME}..."
+    (cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
+  fi
+  BINARY_NAME='reviewdog'
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
+  else
+    BINARY_PATH="${TMPDIR}/reviewdog"
+  fi
+
+  if [ "${UNAME_OS}" = "windows" ]; then
+    case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
+    case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
+  fi
+
+  if [ ! -f "${BINARY_PATH}" ]; then
+    log_crit "Binary not found: ${BINARY_PATH}"
+    log_crit "Listing contents of ${TMPDIR} ..."
+    if command -v find >/dev/null 2>&1; then
+      cd "${TMPDIR}" && find .
+    else
+      cd "${TMPDIR}" && ls -R .
+    fi
+    return 1
+  fi
+  # Install the binary
+  INSTALL_PATH="${BINDIR}/${BINARY_NAME}"
+
+  if [ "$DRY_RUN" = "1" ]; then
+    log_info "[DRY RUN] ${BINARY_NAME} dry-run installation succeeded! (Would install to: ${INSTALL_PATH})"
+  else
+    log_info "Installing binary to ${INSTALL_PATH}"
+    test ! -d "${BINDIR}" && install -d "${BINDIR}"
+    install "${BINARY_PATH}" "${INSTALL_PATH}"
+    log_info "${BINARY_NAME} installation complete!"
+  fi
+}
+
+# --- Configuration  ---
+NAME='reviewdog'
+REPO='reviewdog/reviewdog'
+EXT='.tar.gz'
+
+# use in logging routines
+log_prefix() {
+  echo "${REPO}"
+}
+
+parse_args "$@"
+
+# --- Determine target platform ---
+OS="${BINSTALLER_OS:-$(uname_os)}"
+UNAME_OS="${OS}"
+
+ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
+UNAME_ARCH="${ARCH}"
+log_info "Detected Platform: ${OS}/${ARCH}"
+
+# --- Validate platform ---
+uname_os_check "$OS"
+uname_arch_check "$ARCH"
+
+tag_to_version
+
+resolve_asset_filename
+
+execute
diff --git install.sh install.sh
index 6738103..728bfef 100755
--- install.sh
+++ install.sh
@@ -13,25 +13,16 @@ if [ -z "${TEMP}" ]; then
   fi
 fi
 
-INSTALL_SCRIPT='https://raw.githubusercontent.com/reviewdog/reviewdog/fd59714416d6d9a1c0692d872e38e7f8448df4fc/install.sh'
+INSTALL_SCRIPT="$GITHUB_ACTION_PATH/install-reviewdog.sh"
 if [ "${VERSION}" = 'nightly' ]; then
-  INSTALL_SCRIPT='https://raw.githubusercontent.com/reviewdog/nightly/30fccfe9f47f7e6fd8b3c38aa0da11a6c9f04de7/install.sh'
+  INSTALL_SCRIPT="$GITHUB_ACTION_PATH/install-nightly.sh"
   VERSION='latest'
 fi
 
 mkdir -p "${TEMP}/reviewdog/bin"
 
 echo '::group::🐶 Installing reviewdog ... https://github.com/reviewdog/reviewdog'
-(
-  if command -v curl 2>&1 >/dev/null; then
-    curl -sfL "${INSTALL_SCRIPT}"
-  elif command -v wget 2>&1 >/dev/null; then
-    wget -O - "${INSTALL_SCRIPT}"
-  else
-    echo "curl or wget is required" >&2
-    exit 1
-  fi
-) | sh -s -- -b "${TEMP}/reviewdog/bin" "${VERSION}" 2>&1
+cat "${INSTALL_SCRIPT}" | sh -s -- -b "${TEMP}/reviewdog/bin" "${VERSION}" 2>&1
 echo '::endgroup::'
 
 echo "${TEMP}/reviewdog/bin" >>"${GITHUB_PATH}"
diff --git a/scripts/binst.sh b/scripts/binst.sh
new file mode 100755
index 0000000..867918f
--- /dev/null
+++ scripts/binst.sh
@@ -0,0 +1,574 @@
+#!/bin/sh
+# Code generated by binstaller. DO NOT EDIT.
+# This script runs binst directly without installing
+#
+set -e
+usage() {
+  this=$1
+  cat <<EOF
+$this: download and run ${NAME} from ${REPO}
+
+Usage: $this [-d] [tag] -- [binary arguments]
+  -d turns on debug logging
+   [tag] is a tag from
+   https://github.com/binary-install/binstaller/releases
+   If tag is missing, then the latest will be used.
+
+   This script downloads and runs binst directly
+   Pass arguments after --:
+   Example: $this -- --help
+   Example: $this -- --version
+
+ Generated by binstaller
+  https://github.com/binary-install/binstaller
+EOF
+  exit 2
+}
+
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+https://github.com/client9/shlib - portable posix shell functions
+Public domain - http://unlicense.org
+https://github.com/client9/shlib/blob/master/LICENSE.md
+but credit (and pull requests) appreciated.
+------------------------------------------------------------------------
+EOF
+is_command() {
+  command -v "$1" >/dev/null
+}
+echoerr() {
+  echo "$@" 1>&2
+}
+_logp=6
+log_set_priority() {
+  _logp="$1"
+}
+log_priority() {
+  if test -z "$1"; then
+    echo "$_logp"
+    return
+  fi
+  [ "$1" -le "$_logp" ]
+}
+log_tag() {
+  case $1 in
+    0) echo "emerg" ;;
+    1) echo "alert" ;;
+    2) echo "crit" ;;
+    3) echo "err" ;;
+    4) echo "warning" ;;
+    5) echo "notice" ;;
+    6) echo "info" ;;
+    7) echo "debug" ;;
+    *) echo "$1" ;;
+  esac
+}
+log_debug() {
+  log_priority 7 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 7)" "$@"
+}
+log_info() {
+  log_priority 6 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 6)" "$@"
+}
+log_err() {
+  log_priority 3 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 3)" "$@"
+}
+log_crit() {
+  log_priority 2 || return 0
+  echoerr "$(log_prefix)" "$(log_tag 2)" "$@"
+}
+uname_os() {
+  os=$(uname -s | tr '[:upper:]' '[:lower:]')
+  case "$os" in
+    msys*) os="windows" ;;
+    mingw*) os="windows" ;;
+    cygwin*) os="windows" ;;
+  esac
+  if [ "$os" = "sunos" ]; then
+    if [ "$(uname -o)" = "illumos" ]; then
+      os="illumos"
+    else
+      os="solaris"
+    fi
+  fi
+  echo "$os"
+}
+uname_arch() {
+  arch=$(uname -m)
+  case $arch in
+    x86_64) arch="amd64" ;;
+    i86pc) arch="amd64" ;;
+    x86) arch="386" ;;
+    i686) arch="386" ;;
+    i386) arch="386" ;;
+    aarch64) arch="arm64" ;;
+    armv5*) arch="armv5" ;;
+    armv6*) arch="armv6" ;;
+    armv7*) arch="armv7" ;;
+  esac
+  echo "${arch}"
+}
+uname_os_check() {
+  os=$(uname_os)
+  case "$os" in
+    darwin) return 0 ;;
+    dragonfly) return 0 ;;
+    freebsd) return 0 ;;
+    linux) return 0 ;;
+    android) return 0 ;;
+    midnightbsd) return 0 ;;
+    nacl) return 0 ;;
+    netbsd) return 0 ;;
+    openbsd) return 0 ;;
+    plan9) return 0 ;;
+    solaris) return 0 ;;
+    illumos) return 0 ;;
+    windows) return 0 ;;
+  esac
+  log_crit "uname_os_check '$(uname -s)' got converted to '$os' which is not a GOOS value. Please file bug at https://github.com/client9/shlib"
+  return 1
+}
+uname_arch_check() {
+  arch=$(uname_arch)
+  case "$arch" in
+    386) return 0 ;;
+    amd64) return 0 ;;
+    arm64) return 0 ;;
+    armv5) return 0 ;;
+    armv6) return 0 ;;
+    armv7) return 0 ;;
+    ppc64) return 0 ;;
+    ppc64le) return 0 ;;
+    mips) return 0 ;;
+    mipsle) return 0 ;;
+    mips64) return 0 ;;
+    mips64le) return 0 ;;
+    s390x) return 0 ;;
+    amd64p32) return 0 ;;
+  esac
+  log_crit "uname_arch_check '$(uname -m)' got converted to '$arch' which is not a GOARCH value.  Please file bug report at https://github.com/client9/shlib"
+  return 1
+}
+cat /dev/null <<EOF
+------------------------------------------------------------------------
+End of functions from https://github.com/client9/shlib
+------------------------------------------------------------------------
+EOF
+
+
+hash_sha256() {
+  TARGET=${1:-/dev/stdin}
+  if is_command gsha256sum; then
+    hash=$(gsha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command sha256sum; then
+    hash=$(sha256sum "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command shasum; then
+    hash=$(shasum -a 256 "$TARGET" 2>/dev/null) || return 1
+    echo "$hash" | cut -d ' ' -f 1
+  elif is_command openssl; then
+    hash=$(openssl dgst -sha256 "$TARGET") || return 1
+    echo "$hash" | cut -d ' ' -f 2
+  else
+    log_crit "hash_sha256 unable to find command to compute sha-256 hash"
+    return 1
+  fi
+}
+
+hash_compute() {
+  hash_sha256 "$1"
+}
+
+
+untar() {
+  tarball=$1
+  strip_components=${2:-0} # default 0
+  case "${tarball}" in
+  *.tar.gz | *.tgz) tar --no-same-owner -xzf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.xz) tar --no-same-owner -xJf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar.bz2) tar --no-same-owner -xjf "${tarball}" --strip-components "${strip_components}" ;;
+  *.tar) tar --no-same-owner -xf "${tarball}" --strip-components "${strip_components}" ;;
+  *.gz) gunzip "${tarball}" ;;
+  *.zip)
+    # unzip doesn't have a standard --strip-components
+    # Workaround: extract to a subdir and move contents up if stripping
+    if [ "$strip_components" -gt 0 ]; then
+      extract_dir=$(basename "${tarball%.zip}")_extracted
+      unzip -q "${tarball}" -d "${extract_dir}"
+      # Move contents of the *first* directory found inside extract_dir up
+      # This assumes wrap_in_directory=true convention
+      first_subdir=$(find "${extract_dir}" -mindepth 1 -maxdepth 1 -type d -print -quit)
+      if [ -n "$first_subdir" ]; then
+        # Move all contents (* includes hidden files)
+        mv "${first_subdir}"/* .
+        # Optionally remove the now-empty subdir and the extract_dir
+        rmdir "${first_subdir}"
+        rmdir "${extract_dir}"
+      else
+        log_warn "Could not find subdirectory in zip to strip components from ${extract_dir}"
+        # Files are extracted in current dir anyway, proceed
+      fi
+    else
+      unzip -q "${tarball}"
+    fi
+    ;;
+  *)
+    log_err "untar unknown archive format for ${tarball}"
+    return 1
+    ;;
+  esac
+}
+
+
+
+hash_verify() {
+  TARGET_PATH=$1
+  SUMFILE=$2
+  if [ -z "${SUMFILE}" ]; then
+    log_err "hash_verify checksum file not specified in arg2"
+    return 1
+  fi
+  got=$(hash_compute "$TARGET_PATH")
+  if [ -z "${got}" ]; then
+    log_err "failed to calculate hash: ${TARGET_PATH}"
+    return 1
+  fi
+
+  BASENAME=${TARGET_PATH##*/}
+
+  # Check for line matches in checksum file
+  # Format: "<hash>  <filename>" or "<hash> *<filename>"
+  # Filename may include path prefix (e.g., "deployment/m2/file.tar.gz")
+  while IFS= read -r line || [ -n "$line" ]; do
+    # Normalize tabs to spaces
+    line=$(echo "$line" | tr '\t' ' ')
+
+    # Remove trailing spaces for hash-only line check
+    line_trimmed=$(echo "$line" | sed 's/[[:space:]]*$//')
+
+    # Check for hash-only line (no filename) - early return
+    if [ "$line_trimmed" = "$got" ]; then
+      return 0
+    fi
+
+    # Extract hash and filename parts
+    # First field is the hash, rest is filename (which may contain spaces)
+    line_hash=$(echo "$line" | cut -d' ' -f1)
+
+    # Skip if hash doesn't match
+    if [ "$line_hash" != "$got" ]; then
+      continue
+    fi
+
+    # Hash matches, now check filename
+    # Remove the hash part from the beginning of the line
+    line_rest="${line#"$got"}"
+    # Remove leading spaces
+    while [ "${line_rest#[ ]}" != "$line_rest" ]; do
+      line_rest="${line_rest#[ ]}"
+    done
+
+    # Remove leading asterisk if present (binary mode indicator)
+    if [ "${line_rest#\*}" != "$line_rest" ]; then
+      line_rest="${line_rest#\*}"
+    fi
+
+    # Extract just the filename without any path
+    line_filename="${line_rest##*/}"
+
+    # Check if the filename matches
+    if [ "$line_filename" = "$BASENAME" ]; then
+      return 0
+    fi
+  done < "$SUMFILE"
+
+  log_err "hash_verify checksum for '$TARGET_PATH' did not verify"
+  log_err "  Expected hash: ${got}"
+  log_err "  Checksum file content:"
+  cat "$SUMFILE" >&2
+  return 1
+}
+
+# GitHub HTTP download functions with GITHUB_TOKEN support
+github_http_download_curl() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "Authorization: Bearer $GITHUB_TOKEN" -H "$header" -o "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      curl -fsSL -o "$local_file" "$source_url"
+    else
+      curl -fsSL -H "$header" -o "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download_wget() {
+  local_file=$1
+  source_url=$2
+  header=$3
+  if [ -n "$GITHUB_TOKEN" ]; then
+    log_debug "Using GITHUB_TOKEN for authentication"
+    if [ -z "$header" ]; then
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" -O "$local_file" "$source_url"
+    else
+      wget -q --header "Authorization: Bearer $GITHUB_TOKEN" --header "$header" -O "$local_file" "$source_url"
+    fi
+  else
+    if [ -z "$header" ]; then
+      wget -q -O "$local_file" "$source_url"
+    else
+      wget -q --header "$header" -O "$local_file" "$source_url"
+    fi
+  fi
+}
+github_http_download() {
+  log_debug "github_http_download $2"
+  if is_command curl; then
+    github_http_download_curl "$@"
+    return
+  elif is_command wget; then
+    github_http_download_wget "$@"
+    return
+  fi
+  log_crit "github_http_download unable to find wget or curl"
+  return 1
+}
+github_http_copy() {
+  tmp=$(mktemp)
+  github_http_download "${tmp}" "$@" || return 1
+  body=$(cat "$tmp")
+  rm -f "${tmp}"
+  echo "$body"
+}
+github_release() {
+  owner_repo=$1
+  version=$2
+  test -z "$version" && version="latest"
+  giturl="https://github.com/${owner_repo}/releases/${version}"
+  json=$(github_http_copy "$giturl" "Accept:application/json")
+  test -z "$json" && return 1
+  version=$(echo "$json" | tr -s '\n' ' ' | sed 's/.*"tag_name":"//' | sed 's/".*//')
+  test -z "$version" && return 1
+  echo "$version"
+}
+
+# --- Embedded Checksums (Format: VERSION:FILENAME:HASH) ---
+EMBEDDED_CHECKSUMS=""
+
+# Find embedded checksum for a given version and filename
+find_embedded_checksum() {
+  version="$1"
+  filename="$2"
+  echo "$EMBEDDED_CHECKSUMS" | grep -E "^${version}:${filename}:" | cut -d':' -f3
+}
+parse_args() {
+  SEPARATOR_FOUND=0
+  TOOL_ARGS=""
+  while [ $# -gt 0 ]; do
+    case "$1" in
+    -d) log_set_priority 10 ;;
+    -h | --help | \?) usage "$0" ;;
+    -x) set -x ;;
+    --)
+      SEPARATOR_FOUND=1
+      shift
+      break
+      ;;
+    -*)
+      usage "$0"
+      ;;
+    *)
+      if [ $SEPARATOR_FOUND -eq 0 ]; then
+        # First non-flag argument is the tag/version
+        TAG="$1"
+      else
+        TOOL_ARGS="$1 $TOOL_ARGS"
+      fi
+      ;;
+    esac
+    shift
+  done
+
+  # Collect remaining arguments after --
+  while [ $# -gt 0 ]; do
+    TOOL_ARGS="$TOOL_ARGS $1"
+    shift
+  done
+  TAG="${TAG:-latest}"
+}
+tag_to_version() {
+  if [ "$TAG" = "latest" ]; then
+    log_info "checking GitHub for latest tag"
+    REALTAG=$(github_release "${REPO}" "${TAG}") && true
+    test -n "$REALTAG" || {
+      log_crit "Could not determine latest tag for ${REPO}"
+      exit 1
+    }
+  else
+    # Assume TAG is a valid tag/version string
+    REALTAG="$TAG"
+  fi
+  if test -z "$REALTAG"; then
+    log_crit "unable to find '${TAG}' - use 'latest' or see https://github.com/${REPO}/releases for details"
+    exit 1
+  fi
+  VERSION=${REALTAG#v} # Strip leading 'v'
+  TAG="$REALTAG"       # Use the resolved tag
+  log_info "Resolved version: ${VERSION} (tag: ${TAG})"
+}
+
+
+capitalize() {
+  input="$1"
+  first_char=$(printf "%s" "$input" | cut -c1)
+  first_upper=$(printf "%s" "$first_char" | tr '[:lower:]' '[:upper:]')
+  printf "%s%s\n" "$first_upper" "$(printf "%s" "$input" | cut -c2-)"
+}
+
+resolve_asset_filename() {
+
+  OS="$(capitalize "${OS}")"
+  # --- Apply Rules ---
+  ASSET_FILENAME=""
+  if [ "${UNAME_ARCH}" = 'amd64' ] && true
+  then
+    ARCH='x86_64'
+  fi
+  if [ "${UNAME_ARCH}" = '386' ] && true
+  then
+    ARCH='i386'
+  fi
+  if [ "${UNAME_OS}" = 'windows' ] && true
+  then
+    EXT='.zip'
+  fi
+  if [ -z "${ASSET_FILENAME}" ]; then
+    ASSET_FILENAME="${NAME}_${OS}_${ARCH}${EXT}"
+  fi
+}
+# Cleanup function to remove temporary files
+cleanup() {
+  if [ -n "$TMPDIR" ] && [ -d "$TMPDIR" ]; then
+    log_debug "Cleaning up temporary directory: $TMPDIR"
+    rm -rf -- "$TMPDIR"
+  fi
+}
+
+execute() {
+  STRIP_COMPONENTS=0
+  CHECKSUM_FILENAME="checksums.txt"
+
+  # --- Construct URLs ---
+  GITHUB_DOWNLOAD="https://github.com/${REPO}/releases/download"
+  ASSET_URL="${GITHUB_DOWNLOAD}/${TAG}/${ASSET_FILENAME}"
+  CHECKSUM_URL=""
+  if [ -n "$CHECKSUM_FILENAME" ]; then
+    CHECKSUM_URL="${GITHUB_DOWNLOAD}/${TAG}/${CHECKSUM_FILENAME}"
+  fi
+
+  # --- Download and Verify ---
+  TMPDIR=$(mktemp -d)
+  trap cleanup EXIT HUP INT TERM
+  log_debug "Downloading files into ${TMPDIR}"
+  log_info "Downloading ${ASSET_URL}"
+  github_http_download "${TMPDIR}/${ASSET_FILENAME}" "${ASSET_URL}"
+
+  # Try to find embedded checksum first
+  EMBEDDED_HASH=$(find_embedded_checksum "$VERSION" "$ASSET_FILENAME")
+
+  if [ -n "$EMBEDDED_HASH" ]; then
+    log_info "Using embedded checksum for verification"
+
+    # Verify using embedded hash
+    got=$(hash_compute "${TMPDIR}/${ASSET_FILENAME}")
+    if [ "$got" != "$EMBEDDED_HASH" ]; then
+      log_crit "Checksum verification failed for ${ASSET_FILENAME}"
+      log_crit "Expected: ${EMBEDDED_HASH}"
+      log_crit "Got: ${got}"
+      return 1
+    fi
+    log_info "Checksum verification successful"
+  elif [ -n "$CHECKSUM_URL" ]; then
+    # Fall back to downloading checksum file
+    log_info "Downloading checksums from ${CHECKSUM_URL}"
+    github_http_download "${TMPDIR}/${CHECKSUM_FILENAME}" "${CHECKSUM_URL}"
+    log_info "Verifying checksum ..."
+    hash_verify "${TMPDIR}/${ASSET_FILENAME}" "${TMPDIR}/${CHECKSUM_FILENAME}"
+  else
+    log_info "No checksum found, skipping verification."
+  fi
+
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    log_debug "Target is raw binary"
+  else
+    log_info "Extracting ${ASSET_FILENAME}..."
+    (cd "${TMPDIR}" && untar "${ASSET_FILENAME}" "${STRIP_COMPONENTS}")
+  fi
+  BINARY_NAME='binst'
+  if [ -z "${EXT}" ] || [ "${EXT}" = ".exe" ]; then
+    BINARY_PATH="${TMPDIR}/${ASSET_FILENAME}"
+  else
+    BINARY_PATH="${TMPDIR}/binst"
+  fi
+
+  if [ "${UNAME_OS}" = "windows" ]; then
+    case "${BINARY_NAME}" in *.exe) ;; *) BINARY_NAME="${BINARY_NAME}.exe" ;; esac
+    case "${BINARY_PATH}" in *.exe) ;; *) BINARY_PATH="${BINARY_PATH}.exe" ;; esac
+  fi
+
+  if [ ! -f "${BINARY_PATH}" ]; then
+    log_crit "Binary not found: ${BINARY_PATH}"
+    log_crit "Listing contents of ${TMPDIR} ..."
+    if command -v find >/dev/null 2>&1; then
+      cd "${TMPDIR}" && find .
+    else
+      cd "${TMPDIR}" && ls -R .
+    fi
+    return 1
+  fi
+  # Make binary executable for runner script
+  chmod +x "${BINARY_PATH}"
+  # Run the binary directly with provided arguments
+  log_info "Running ${BINARY_NAME}${TOOL_ARGS:+ with arguments:$TOOL_ARGS}"
+  exec "${BINARY_PATH}" $TOOL_ARGS
+}
+
+# --- Configuration  ---
+NAME='binst'
+REPO='binary-install/binstaller'
+EXT='.tar.gz'
+
+# use in logging routines
+log_prefix() {
+  echo "${REPO}"
+}
+
+parse_args "$@"
+
+# --- Determine target platform ---
+OS="${BINSTALLER_OS:-$(uname_os)}"
+UNAME_OS="${OS}"
+
+ARCH="${BINSTALLER_ARCH:-$(uname_arch)}"
+UNAME_ARCH="${ARCH}"
+log_info "Detected Platform: ${OS}/${ARCH}"
+
+# --- Validate platform ---
+uname_os_check "$OS"
+uname_arch_check "$ARCH"
+
+tag_to_version
+
+resolve_asset_filename
+
+execute
diff --git a/scripts/gen.sh b/scripts/gen.sh
new file mode 100755
index 0000000..f0bad7c
--- /dev/null
+++ scripts/gen.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+BINST_VERSION="v0.7.6"
+./scripts/binst.sh "${BINST_VERSION}" -- gen --config .config/binstaller/reviewdog.yml -o install-reviewdog.sh
+./scripts/binst.sh "${BINST_VERSION}" -- gen --config .config/binstaller/reviewdog-nightly.yml -o install-nightly.sh

Description

Replaces remote install script downloads with locally vendored install scripts generated by binstaller. Instead of curl/wget-fetching install.sh from raw GitHub URLs at runtime, the action now ships install-reviewdog.sh and install-nightly.sh directly, sourced via $GITHUB_ACTION_PATH. Also bumps all CI dependency pins (actions/checkout v5, action-bumpr v1.11.4, action-update-semver v1.5.1, various reviewdog actions) and adds gitsign for signed commits in depup workflow. README updated to pin action references by SHA.

Possible Issues

  1. cat "${INSTALL_SCRIPT}" | sh -s -- — UUOC, but more importantly: if $GITHUB_ACTION_PATH is unset (e.g., script invoked outside GitHub Actions), this silently fails or errors. Previous code had a curl/wget fallback that worked standalone.

  2. log_warn undefined — Both install-reviewdog.sh and install-nightly.sh call log_warn inside the untar() zip handling path, but only log_debug, log_info, log_err, log_crit are defined. Will cause a runtime error if a zip with strip_components > 0 hits the else branch.

  3. local keyword in POSIX shgithub_http_download_curl and github_http_download_wget use local_file as a variable name (not local keyword), so this is fine, but worth noting the scripts declare #!/bin/sh and use local-like patterns that are POSIX-compatible.

  4. find_embedded_checksum regex injection$version and $filename are passed directly into grep -E pattern. Filenames containing regex metacharacters (., +) could match unintended lines. Low risk given controlled naming, but still a correctness concern.

  5. Dead cleanup() function — Both install scripts define cleanup() but the execute() function uses an inline trap 'rm -rf -- "$TMPDIR"' instead of trap cleanup. The function is unreachable.

  6. Three ~550-line generated scriptsinstall-reviewdog.sh, install-nightly.sh, and scripts/binst.sh share ~95% identical code. No dedup mechanism; drift risk if manually edited despite "DO NOT EDIT" headers.

Security Hotspots

  1. GITHUB_TOKEN leaked in debug logslog_debug "Using GITHUB_TOKEN for authentication" is benign, but the curl/wget commands with -H "Authorization: Bearer $GITHUB_TOKEN" could appear in process listings or set -x traces (enabled by -x flag). Token exposure via /proc or CI logs.

  2. Checksum file downloaded over HTTPS from same release — Both checksum and asset come from the same GitHub release. A compromised release can serve matching checksums. Previous approach had the same issue; EMBEDDED_CHECKSUMS is empty string, so embedded path never activates — always falls through to downloaded checksums.

  3. exec "${BINARY_PATH}" $TOOL_ARGS in scripts/binst.sh$TOOL_ARGS is unquoted, subject to word splitting/globbing. Crafted arguments could cause unexpected behavior.

Changes

Changes

  • .config/binstaller/reviewdog.yml / reviewdog-nightly.yml — New binstaller config specs for generating install scripts.
  • install-reviewdog.sh / install-nightly.sh — New ~550-line generated shell scripts replacing remote install script fetching. Include SHA256 checksum verification, platform detection, archive extraction.
  • scripts/binst.sh — Generated runner script for binstaller tool itself, used by scripts/gen.sh.
  • scripts/gen.sh — Codegen entrypoint invoking binst to regenerate both install scripts.
  • install.sh — Switches from curl/wget remote fetch to cat "$GITHUB_ACTION_PATH/install-*.sh" | sh -s.
  • .github/workflows/*.yml — Bumps all action pins to newer SHAs; adds id-token/contents/pull-requests permissions and gitsign setup to depup workflow.
  • README.md — Pins action-setup usage examples to SHA d8edfce3, updates example reviewdog_version to v0.21.0.
sequenceDiagram
    participant GHA as GitHub Actions Runner
    participant install as install.sh
    participant script as install-reviewdog.sh
    participant GH as GitHub Releases

    GHA->>install: Run action (VERSION input)
    install->>install: Determine script path via $GITHUB_ACTION_PATH
    alt VERSION == nightly
        install->>script: cat install-nightly.sh | sh -s -- -b <bindir> latest
    else
        install->>script: cat install-reviewdog.sh | sh -s -- -b <bindir> <VERSION>
    end
    script->>script: Detect OS/ARCH
    script->>GH: Resolve latest tag (if needed)
    GH-->>script: Tag/version
    script->>script: Resolve asset filename
    script->>GH: Download asset tarball
    GH-->>script: Asset binary
    script->>GH: Download checksums.txt
    GH-->>script: Checksum file
    script->>script: Verify SHA256
    script->>script: Extract & install binary
    script-->>install: reviewdog installed
    install->>GHA: Add bin to GITHUB_PATH
Loading

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants