fix(cli): harden bytelyst-cli env loading, pagination, and HTTP checks

- .env via 'set -a; . ./.env; set +a' (handles quoted values/spaces safely)
- printf for the GITHUB_TOKEN message so the newline renders
- gh_get_all: paginate all pages (per_page=100) and verify HTTP 200 before jq;
  rewire list-public/list-private/check-collaborators through it
- fix SC2199 whitelist membership (explicit loop, no substring false-matches)
- shell-ci: gate shellcheck on bytelyst-cli + run agent-queue self-test
This commit is contained in:
saravanakumardb1 2026-05-28 22:30:08 -07:00
parent 4239648876
commit 76104bda84
2 changed files with 64 additions and 21 deletions

View File

@ -37,20 +37,22 @@ jobs:
apt-get install -y -qq shellcheck
shellcheck --version
- name: shellcheck agent-queue (errors fail the build)
- name: shellcheck (errors fail the build)
run: |
shellcheck --severity=error --shell=bash agent-queue/agent-queue.sh
shellcheck --severity=error --shell=bash \
agent-queue/agent-queue.sh \
agent-queue/selftest.sh \
bytelyst-cli.sh
- name: shellcheck bytelyst-cli (non-gating — legacy known issues)
continue-on-error: true
run: |
shellcheck --severity=error --shell=bash bytelyst-cli.sh
- name: bash syntax check (gating, both scripts)
- name: bash syntax check (gating, all scripts)
run: |
bash -n agent-queue/agent-queue.sh
bash -n agent-queue/selftest.sh
bash -n bytelyst-cli.sh
- name: agent-queue self-test (no-op engine cycle)
run: ./agent-queue/selftest.sh
- name: node syntax check (dashboard)
run: node --check agent-queue/dashboard.mjs

View File

@ -35,17 +35,49 @@ for tool in "${REQUIRED_TOOLS[@]}"; do
fi
done
# Load .env if present
# Load .env if present. `set -a` exports everything sourced; this safely handles
# quoted values and spaces, unlike `export $(grep ... | xargs)`.
if [[ -f .env ]]; then
export $(grep -v '^#' .env | xargs)
set -a
# shellcheck disable=SC1091
. ./.env
set +a
fi
# Validate GITHUB_TOKEN
if [[ -z "$GITHUB_TOKEN" ]]; then
echo "${RED}❌ Error: GITHUB_TOKEN is not set.\nSet it in your environment (e.g., export GITHUB_TOKEN=... in ~/.zshrc, ~/.bashrc, or .env).${RESET}"
# Validate GITHUB_TOKEN (printf so the newline renders, unlike echo "...\n...")
if [[ -z "${GITHUB_TOKEN:-}" ]]; then
printf '%s❌ Error: GITHUB_TOKEN is not set.\nSet it in your environment (e.g. export GITHUB_TOKEN=... in ~/.zshrc, ~/.bashrc, or .env).%s\n' "$RED" "$RESET" >&2
exit 1
fi
# gh_get_all <url> -> echo one JSON array combining ALL pages (per_page=100).
# Verifies HTTP 200 on every page before parsing; returns non-zero on API error.
gh_get_all() {
local base="$1" page=1 combined="[]"
local joiner='&'; [[ "$base" == *'?'* ]] || joiner='?'
while :; do
local resp http body n
resp=$(curl -sS -w $'\n%{http_code}' \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"${base}${joiner}per_page=100&page=${page}")
http="${resp##*$'\n'}"
body="${resp%$'\n'*}"
if [[ "$http" != "200" ]]; then
printf '%s❌ GitHub API error (HTTP %s) for %s%s\n' "$RED" "$http" "$base" "$RESET" >&2
printf '%s' "$body" | jq -r '.message? // empty' >&2 2>/dev/null || true
return 1
fi
n=$(printf '%s' "$body" | jq 'length' 2>/dev/null || echo 0)
[[ "$n" -eq 0 ]] && break
combined=$(jq -s 'add' <(printf '%s' "$combined") <(printf '%s' "$body"))
[[ "$n" -lt 100 ]] && break
page=$((page+1))
[[ "$page" -gt 100 ]] && break
done
printf '%s' "$combined"
}
usage() {
echo "${BLUE}Bytelyst CLI - Unified GitHub DevOps Tool${RESET}"
echo ""
@ -75,8 +107,9 @@ list_public_repos() {
echo "${RED}❌ Please provide --user <username>.${RESET}"; exit 1
fi
echo "${BLUE}🔍 Fetching all public repositories for user: $user...${RESET}"
local response=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/users/$user/repos?per_page=100&type=public")
local repos=$(echo "$response" | jq -r '.[].full_name')
local json repos
json=$(gh_get_all "https://api.github.com/users/$user/repos?type=public") || exit 1
repos=$(printf '%s' "$json" | jq -r '.[].full_name')
if [[ -z "$repos" ]]; then
echo "${YELLOW}🚫 No public repositories found for user.${RESET}"
else
@ -97,8 +130,9 @@ list_private_repos() {
echo "${RED}❌ Please provide --org <orgname>.${RESET}"; exit 1
fi
echo "${BLUE}🔍 Fetching all private repositories for org: $org...${RESET}"
local response=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/orgs/$org/repos?per_page=100&type=private")
local repos=$(echo "$response" | jq -r '.[].full_name')
local json repos
json=$(gh_get_all "https://api.github.com/orgs/$org/repos?type=private") || exit 1
repos=$(printf '%s' "$json" | jq -r '.[].full_name')
if [[ -z "$repos" ]]; then
echo "${YELLOW}🚫 No private repositories found for org.${RESET}"
else
@ -127,12 +161,19 @@ check_collaborators() {
fi
for repo in "${repos[@]}"; do
echo "${BLUE}🔍 Checking repo: $repo${RESET}"
local collaborators=$(curl -s -H "Authorization: token $GITHUB_TOKEN" "https://api.github.com/repos/$org/$repo/collaborators" | jq -r '.[].login')
local cjson collaborators
cjson=$(gh_get_all "https://api.github.com/repos/$org/$repo/collaborators") \
|| { echo "${YELLOW}⚠️ Skipping $repo (API error).${RESET}"; continue; }
collaborators=$(printf '%s' "$cjson" | jq -r '.[].login')
local non_whitelisted=()
for collab in $collaborators; do
if [[ ! " ${whitelist[@]} " =~ " ${collab} " ]]; then
non_whitelisted+=("$collab")
fi
# explicit membership test (avoids the array-concatenation pitfall of
# [[ " ${whitelist[@]} " =~ " $collab " ]], which false-matches substrings)
local is_white=false w
for w in "${whitelist[@]}"; do
[[ "$w" == "$collab" ]] && { is_white=true; break; }
done
$is_white || non_whitelisted+=("$collab")
done
if [[ ${#non_whitelisted[@]} -gt 0 ]]; then
echo "${YELLOW}🚨 Repository: $repo${RESET}"