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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 22 additions & 17 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

jobs:
test:
name: Unit tests
name: Test · Format · Lint · Dead code
runs-on: ubuntu-latest
permissions:
pull-requests: write
Expand Down Expand Up @@ -36,22 +36,6 @@
lcov-file: coverage/lcov.info
continue-on-error: true

quality:
name: Format · Lint · Dead code
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup Bun
uses: oven-sh/setup-bun@3d267786b128fe76c2f16a390aa2448b815359f3 # v2.1.2
with:
bun-version: latest

- name: Install dependencies
run: bun install --frozen-lockfile

- name: Check formatting (oxfmt)
run: bun run format:check

Expand All @@ -60,3 +44,24 @@

- name: Dead code detection (knip)
run: bun run knip

test-bats:
name: Shell tests (bats)
runs-on: ubuntu-latest

steps:
- name: Checkout
uses: actions/checkout@v6

- name: Setup Bats
uses: bats-core/bats-action@4.0.0
Comment thread Fixed
with:
support-install: false
assert-install: false
detik-install: false
file-install: false
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Run install.sh shell tests
shell: bash
run: bats install.test.bats
Comment thread Fixed
Comment thread Fixed
88 changes: 44 additions & 44 deletions bun.lock

Large diffs are not rendered by default.

37 changes: 37 additions & 0 deletions github-code-search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { buildOutput } from "./src/output.ts";
import { groupByTeamPrefix, flattenTeamSections } from "./src/group.ts";
import { checkForUpdate } from "./src/upgrade.ts";
import { runInteractive } from "./src/tui.ts";
import { generateCompletion, detectShell } from "./src/completions.ts";
import type { OutputFormat, OutputType } from "./src/types.ts";

// Version + build metadata injected at compile time via --define (see build.ts).
Expand Down Expand Up @@ -349,6 +350,42 @@ program
process.stdout.write(`error: ${e instanceof Error ? e.message : String(e)}\n`);
process.exit(1);
}
const { refreshCompletions } = await import("./src/upgrade.ts");
const refreshedPath = await refreshCompletions(detectShell(), undefined, opts.debug);
if (refreshedPath) {
process.stdout.write(`✓ Shell completions refreshed at ${refreshedPath}\n`);
Comment thread
shouze marked this conversation as resolved.
Outdated
}
process.exit(0);
});

// `completions` subcommand — print a shell completion script to stdout
program
.command("completions")
.description("Print a shell completion script for bash, zsh or fish")
.configureHelp(helpFormatConfig)
.addHelpText(
"after",
helpSection("Documentation:", "https://fulll.github.io/github-code-search/usage/upgrade"),
Comment thread
shouze marked this conversation as resolved.
)
Comment thread
shouze marked this conversation as resolved.
.option(
"--shell <shell>",
["Target shell: bash, zsh or fish.", "Auto-detected from $SHELL when omitted."].join("\n"),
)
.action((opts: { shell?: string }) => {
const shell = opts.shell ?? detectShell();
if (!shell) {
writeFileSync(
2,
`error: could not detect shell. Use --shell bash|zsh|fish to specify it explicitly.\n`,
);
process.exit(1);
}
try {
writeFileSync(1, generateCompletion(shell) + "\n");
} catch (e: unknown) {
writeFileSync(2, `error: ${e instanceof Error ? e.message : String(e)}\n`);
process.exit(1);
}
process.exit(0);
});

Expand Down
179 changes: 118 additions & 61 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,71 +8,128 @@
# INSTALL_DIR destination directory (default: /usr/local/bin)
# VERSION specific version tag to install (default: latest)

set -euo pipefail

REPO="fulll/github-code-search"
BINARY_NAME="github-code-search"
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
GITHUB_API="https://api.github.com"

# ─── Detect OS ────────────────────────────────────────────────────────────────

case "$(uname -s)" in
Linux*) OS="linux" ;;
Darwin*) OS="macos" ;;
MINGW*|MSYS*|CYGWIN*) OS="windows" ;;
*)
echo "error: unsupported OS: $(uname -s)" >&2
exit 1
;;
esac

# ─── Detect architecture ──────────────────────────────────────────────────────

MACHINE="$(uname -m)"
case "$MACHINE" in
x86_64|amd64) ARCH="x64" ;;
arm64|aarch64) ARCH="arm64" ;;
*)
echo "error: unsupported architecture: $MACHINE" >&2
exit 1
;;
esac

ARTIFACT="${BINARY_NAME}-${OS}-${ARCH}"
[ "$OS" = "windows" ] && ARTIFACT="${ARTIFACT}.exe"

# ─── Resolve version ──────────────────────────────────────────────────────────

if [ -n "${VERSION:-}" ]; then
TAG="$VERSION"
else
echo "Detecting latest release…"
TAG=$(curl -fsSL "${GITHUB_API}/repos/${REPO}/releases/latest" \
| grep '"tag_name"' \
| sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')
fi

echo "Installing ${BINARY_NAME} ${TAG} (${OS}/${ARCH})…"
# ─── Shell completions ────────────────────────────────────────────────────────
# Defined early so it can be sourced and tested independently (INSTALL_SH_TEST=1).
# Note: set -euo pipefail is intentionally NOT set here — it is applied only in
# the main installation block below so that sourcing this file for bats tests
# does not alter the caller's shell options.

# ─── Download ─────────────────────────────────────────────────────────────────
install_completions() {
local bin="$1"
local shell_name
shell_name="$(basename "${SHELL:-}")"

DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${TAG}/${ARTIFACT}"
TMP="$(mktemp)"
curl -fsSL --progress-bar -o "$TMP" "$DOWNLOAD_URL"
chmod +x "$TMP"
case "$shell_name" in
fish)
local comp_dir="${XDG_CONFIG_HOME:-$HOME/.config}/fish/completions"
local comp_file="${comp_dir}/github-code-search.fish"
mkdir -p "$comp_dir"
"$bin" completions --shell fish > "$comp_file"
echo ""
echo "✓ Fish completions installed at ${comp_file}"
;;
zsh)
local comp_dir="${ZDOTDIR:-$HOME}/.zfunc"
local comp_file="${comp_dir}/_github-code-search"
mkdir -p "$comp_dir"
"$bin" completions --shell zsh > "$comp_file"
echo ""
echo "✓ Zsh completions installed at ${comp_file}"
echo " Make sure ${comp_dir} is on your fpath. Add to ~/.zshrc if needed:"
echo " fpath=(${comp_dir} \$fpath)"
echo " autoload -Uz compinit && compinit"
;;
bash)
local comp_dir="${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion/completions"
local comp_file="${comp_dir}/github-code-search"
mkdir -p "$comp_dir"
"$bin" completions --shell bash > "$comp_file"
echo ""
echo "✓ Bash completions installed at ${comp_file}"
;;
*)
echo ""
echo " Shell completions are available for bash, zsh and fish."
echo " Run the following to generate your completion script:"
echo " ${BINARY_NAME} completions --shell <bash|zsh|fish>"
;;
esac
}

# ─── Install ──────────────────────────────────────────────────────────────────
# ─── Main installation ────────────────────────────────────────────────────────
# Skipped when sourced for testing (export INSTALL_SH_TEST=1 before sourcing).

DEST="${INSTALL_DIR}/${BINARY_NAME}"
if [ -w "$INSTALL_DIR" ]; then
mv "$TMP" "$DEST"
else
echo " (sudo required for ${INSTALL_DIR})"
sudo mv "$TMP" "$DEST"
fi
if [ -z "${INSTALL_SH_TEST:-}" ]; then
set -euo pipefail

REPO="fulll/github-code-search"
INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
GITHUB_API="https://api.github.com"

# ─── Detect OS ──────────────────────────────────────────────────────────────

case "$(uname -s)" in
Linux*) OS="linux" ;;
Darwin*) OS="macos" ;;
MINGW*|MSYS*|CYGWIN*) OS="windows" ;;
*)
echo "error: unsupported OS: $(uname -s)" >&2
exit 1
;;
esac

# ─── Detect architecture ────────────────────────────────────────────────────

MACHINE="$(uname -m)"
case "$MACHINE" in
x86_64|amd64) ARCH="x64" ;;
arm64|aarch64) ARCH="arm64" ;;
*)
echo "error: unsupported architecture: $MACHINE" >&2
exit 1
;;
esac

ARTIFACT="${BINARY_NAME}-${OS}-${ARCH}"
[ "$OS" = "windows" ] && ARTIFACT="${ARTIFACT}.exe"

# ─── Resolve version ────────────────────────────────────────────────────────

echo "✓ ${BINARY_NAME} ${TAG} installed at ${DEST}"
echo ""
echo " Remember to export your GitHub token:"
echo " export GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx"
if [ -n "${VERSION:-}" ]; then
TAG="$VERSION"
else
echo "Detecting latest release…"
TAG=$(curl -fsSL "${GITHUB_API}/repos/${REPO}/releases/latest" \
| grep '"tag_name"' \
| sed -E 's/.*"tag_name": *"([^"]+)".*/\1/')
fi

echo "Installing ${BINARY_NAME} ${TAG} (${OS}/${ARCH})…"

# ─── Download ───────────────────────────────────────────────────────────────

DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${TAG}/${ARTIFACT}"
TMP="$(mktemp)"
curl -fsSL --progress-bar -o "$TMP" "$DOWNLOAD_URL"
chmod +x "$TMP"

# ─── Install ────────────────────────────────────────────────────────────────

DEST="${INSTALL_DIR}/${BINARY_NAME}"
if [ -w "$INSTALL_DIR" ]; then
mv "$TMP" "$DEST"
else
echo " (sudo required for ${INSTALL_DIR})"
sudo mv "$TMP" "$DEST"
fi

echo "✓ ${BINARY_NAME} ${TAG} installed at ${DEST}"
echo ""
echo " Remember to export your GitHub token:"
echo " export GITHUB_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxx"

install_completions "$DEST"

Comment thread
shouze marked this conversation as resolved.
fi
Loading
Loading