Skip to content

Conversation

@renovate
Copy link
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: 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
Contributor

[puLL-Merge] - reviewdog/[email protected]

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
@github-actions
Copy link
Contributor

[puLL-Merge] - reviewdog/[email protected]

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

This PR updates the reviewdog/action-setup GitHub Action to use locally bundled installer scripts instead of downloading them from remote URLs at runtime. The change introduces new installation scripts generated by binstaller for both the stable reviewdog release and nightly builds, along with configuration files for the binstaller tool.

Key motivations appear to be:

  1. Improved reliability by not depending on external URL availability during action execution
  2. Better security through local script verification
  3. Fixed bugs from the previous godownloader-generated scripts (e.g., handling of .sbom.json files)
  4. Enabling signed commits via gitsign integration in the depup workflow

Possible Issues

  1. Outdated actions/checkout reference: The PR updates actions/checkout from v4.2.2 to what's labeled as v5.0.1 with hash 93cb6efe18208431cddfb8368fd83d5badbf9bfd, but this hash doesn't correspond to any known v5 release of actions/checkout. This appears to be an error that could break workflows.

  2. Large generated code: The installer scripts (~550 lines each) are code-generated and duplicated three times with minor variations. Any bugs or security issues in the template would need to be fixed in multiple places.

  3. USELESS_CAT usage: In install.sh, the change uses cat "${INSTALL_SCRIPT}" | sh -s which is a useless use of cat and could be replaced with sh -s < "${INSTALL_SCRIPT}".

Security Hotspots

  1. GITHUB_TOKEN exposure in logs (install-*.sh, scripts/binst.sh): The scripts include log_debug "Using GITHUB_TOKEN for authentication" which is fine, but the token itself is passed to curl/wget. If debug logging is enabled and something goes wrong, there's potential for token leakage in error messages.

  2. Command injection risk in checksum verification (install-*.sh lines ~260-300): The hash_verify function uses variables like $BASENAME and $TARGET_PATH in string operations without full sanitization. A maliciously crafted filename could potentially cause issues.

  3. Unquoted variable expansion (install-*.sh): exec "${BINARY_PATH}" $TOOL_ARGS in scripts/binst.sh line 560 - $TOOL_ARGS is unquoted, which could lead to word splitting issues with arguments containing spaces.

Privacy Hotspots

None identified.

Changes

Changes

.config/binstaller/reviewdog-nightly.yml & reviewdog.yml

  • New configuration files for the binstaller tool defining how to download and verify reviewdog binaries

.github/workflows/depup.yml

  • Added OIDC permissions for signed commits
  • Added gitsign setup for keyless commit signing
  • Updated action references

.github/workflows/release.yml, reviewdog.yml, test.yml

  • Updated various action references to newer versions (checkout, bumpr, shellcheck, misspell, alex, actionlint)

README.md

  • Updated usage examples to use pinned SHA references instead of @v1
  • Updated example version from v0.20.3 to v0.21.0

install-nightly.sh & install-reviewdog.sh

  • New ~550-line generated shell scripts for installing reviewdog with checksum verification, platform detection, and archive extraction

install.sh

  • Changed from downloading install scripts from URLs to using local bundled scripts via $GITHUB_ACTION_PATH

scripts/binst.sh

  • Runner script for binstaller tool used to generate the install scripts

scripts/gen.sh

  • Helper script to regenerate the install scripts using binstaller v0.7.6
sequenceDiagram
    participant User
    participant GHA as GitHub Action
    participant InstallSh as install.sh
    participant InstallerScript as install-reviewdog.sh
    participant GitHub as GitHub Releases

    User->>GHA: Use reviewdog/action-setup
    GHA->>InstallSh: Execute install.sh
    InstallSh->>InstallSh: Determine script (stable/nightly)
    InstallSh->>InstallerScript: cat script | sh -s
    InstallerScript->>InstallerScript: Detect platform (OS/arch)
    InstallerScript->>GitHub: Fetch latest tag (if needed)
    InstallerScript->>GitHub: Download binary tarball
    InstallerScript->>GitHub: Download checksums.txt
    InstallerScript->>InstallerScript: Verify SHA256 checksum
    InstallerScript->>InstallerScript: Extract and install binary
    InstallerScript->>GHA: Binary installed to GITHUB_PATH
    GHA->>User: reviewdog available
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.

1 participant