Add shellcheck + shfmt linting for scripts

Add CI workflow to run shellcheck + shfmt, plus a scripts/lint-shell.sh helper.

Also apply shfmt formatting and fix initial shellcheck warnings.
This commit is contained in:
joshp123 2026-02-15 15:51:40 -08:00
parent c44d54319e
commit 52f5168cd2
12 changed files with 78 additions and 32 deletions

26
.github/workflows/shell-lint.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: Shell lint
on:
pull_request:
push:
branches:
- main
paths:
- scripts/**
- .github/workflows/shell-lint.yml
jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v27
with:
nix_path: nixpkgs=channel:nixos-unstable
- name: Run shellcheck + shfmt
run: |
nix shell nixpkgs#shellcheck nixpkgs#shfmt -c bash scripts/lint-shell.sh

View File

@ -10,9 +10,9 @@ host="$1"
ids="$(aws ec2 describe-instances \
--filters \
"Name=tag:app,Values=clawdinator" \
"Name=tag:Name,Values=${host}" \
"Name=instance-state-name,Values=running" \
"Name=tag:app,Values=clawdinator" \
"Name=tag:Name,Values=${host}" \
"Name=instance-state-name,Values=running" \
--query 'Reservations[].Instances[].InstanceId' \
--output text)"
@ -22,7 +22,7 @@ if [ -z "${ids}" ] || [ "${ids}" = "None" ]; then
fi
# If multiple instances match, fail loudly.
count="$(wc -w <<<"${ids}" | tr -d ' ')"
count="$(wc -w <<< "${ids}" | tr -d ' ')"
if [ "${count}" != "1" ]; then
echo "expected 1 instance for ${host}, got ${count}: ${ids}" >&2
exit 1

View File

@ -31,13 +31,13 @@ for _ in $(seq 1 300); do
--command-id "${command_id}" \
--details \
--query 'CommandInvocations[0].Status' \
--output text 2>/dev/null || true)"
--output text 2> /dev/null || true)"
case "${status}" in
Success|Cancelled|TimedOut|Failed)
Success | Cancelled | TimedOut | Failed)
break
;;
Pending|InProgress|Delayed|Cancelling|None|"")
Pending | InProgress | Delayed | Cancelling | None | "")
sleep 2
;;
*)
@ -52,9 +52,9 @@ invocation_json="$(aws ssm get-command-invocation \
--instance-id "${instance_id}" \
--output json)"
stdout="$(jq -r '.StandardOutputContent // ""' <<<"${invocation_json}")"
stderr="$(jq -r '.StandardErrorContent // ""' <<<"${invocation_json}")"
final_status="$(jq -r '.Status' <<<"${invocation_json}")"
stdout="$(jq -r '.StandardOutputContent // ""' <<< "${invocation_json}")"
stderr="$(jq -r '.StandardErrorContent // ""' <<< "${invocation_json}")"
final_status="$(jq -r '.Status' <<< "${invocation_json}")"
if [ -n "${stdout}" ]; then
echo "${stdout}"

View File

@ -27,7 +27,7 @@ fi
ext="${image_file##*.}"
ext="$(printf '%s' "${ext}" | tr '[:upper:]' '[:lower:]')"
case "${ext}" in
img|raw)
img | raw)
aws_format="raw"
;;
vhd)

View File

@ -49,13 +49,13 @@ human_age_from_iso() {
return 0
fi
local epoch
epoch="$(date -d "$iso" +%s 2>/dev/null || true)"
epoch="$(date -d "$iso" +%s 2> /dev/null || true)"
human_age_from_epoch "$epoch"
}
deployed_rev="$(cat /run/current-system/configurationRevision 2>/dev/null || true)"
deployed_rev="$(cat /run/current-system/configurationRevision 2> /dev/null || true)"
if [ -z "$deployed_rev" ]; then
deployed_rev="$(nixos-version --json 2>/dev/null | jq -r '.configurationRevision // empty' || true)"
deployed_rev="$(nixos-version --json 2> /dev/null | jq -r '.configurationRevision // empty' || true)"
fi
if [ -z "$deployed_rev" ]; then
deployed_rev="unknown"
@ -76,11 +76,11 @@ openclaw_rev="$(jq -r '.openclaw.rev // empty' "$info")"
last_switch_time=""
if [ -f /var/lib/clawd/deploy/last-switch.time ]; then
last_switch_time="$(tr -d '\n' </var/lib/clawd/deploy/last-switch.time)"
last_switch_time="$(tr -d '\n' < /var/lib/clawd/deploy/last-switch.time)"
fi
last_switch_rev=""
if [ -f /var/lib/clawd/deploy/last-switch.rev ]; then
last_switch_rev="$(tr -d '\n' </var/lib/clawd/deploy/last-switch.rev)"
last_switch_rev="$(tr -d '\n' < /var/lib/clawd/deploy/last-switch.rev)"
fi
echo "clawdinators: $deployed_rev (desired: $desired_rev)"
@ -97,12 +97,12 @@ echo "nixpkgs: $nixpkgs_rev (lock age: $(human_age_from_epoch "$nixpkgs_lm")
echo "openclaw: $openclaw_rev"
# Optional: enrich OpenClaw with commit timestamp/age via GitHub API (requires auth).
if [ -n "$openclaw_rev" ] && command -v gh >/dev/null 2>&1; then
if gh auth status -h github.com >/dev/null 2>&1; then
if [ -n "$openclaw_rev" ] && command -v gh > /dev/null 2>&1; then
if gh auth status -h github.com > /dev/null 2>&1; then
openclaw_date="$(gh api \
-H 'Accept: application/vnd.github+json' \
"/repos/openclaw/openclaw/commits/${openclaw_rev}" \
--jq '.commit.committer.date' 2>/dev/null || true)"
--jq '.commit.committer.date' 2> /dev/null || true)"
if [ -n "$openclaw_date" ]; then
echo " commit: $openclaw_date ($(human_age_from_iso "$openclaw_date") ago)"
fi

View File

@ -10,7 +10,6 @@ if [ -z "${action}" ]; then
exit 1
fi
api_url_file="/etc/clawdinator/control-api-url"
token_file="/run/agenix/clawdinator-control-token"
access_key_file="/run/agenix/clawdinator-control-aws-access-key-id"
secret_key_file="/run/agenix/clawdinator-control-aws-secret-access-key"
@ -33,8 +32,10 @@ control_token="$(cat "${token_file}")"
caller="$(cat "${caller_file}")"
region="${AWS_REGION:-eu-central-1}"
export AWS_ACCESS_KEY_ID="$(cat "${access_key_file}")"
export AWS_SECRET_ACCESS_KEY="$(cat "${secret_key_file}")"
AWS_ACCESS_KEY_ID="$(cat "${access_key_file}")"
AWS_SECRET_ACCESS_KEY="$(cat "${secret_key_file}")"
export AWS_ACCESS_KEY_ID
export AWS_SECRET_ACCESS_KEY
export AWS_REGION="${region}"
if [ "${action}" = "status" ]; then
@ -68,7 +69,7 @@ aws lambda invoke \
--region "${region}" \
--payload "${payload}" \
--cli-binary-format raw-in-base64-out \
"${response_file}" >/dev/null
"${response_file}" > /dev/null
response="$(cat "${response_file}")"
rm -f "${response_file}"

View File

@ -24,6 +24,6 @@ for host in "${hosts[@]}"; do
# Run everything under bash -lc so PATH + profiles behave similarly to an interactive session.
# We also force flakes enabled for safety.
bash scripts/aws-ssm-run.sh "${instance_id}" \
"bash -lc 'set -euo pipefail; export NIX_CONFIG=\"experimental-features = nix-command flakes\"; nixos-rebuild switch --accept-flake-config --flake github:openclaw/clawdinators/${rev}#${host}; systemctl is-active clawdinator; install -d -m 0755 /var/lib/clawd/deploy; date -Is > /var/lib/clawd/deploy/last-switch.time; echo ${rev} > /var/lib/clawd/deploy/last-switch.rev; test \"$(cat /run/current-system/configurationRevision 2>/dev/null || true)\" = \"${rev}\"'"
"bash -lc 'set -euo pipefail; export NIX_CONFIG=\"experimental-features = nix-command flakes\"; nixos-rebuild switch --accept-flake-config --flake github:openclaw/clawdinators/${rev}#${host}; systemctl is-active clawdinator; install -d -m 0755 /var/lib/clawd/deploy; date -Is > /var/lib/clawd/deploy/last-switch.time; echo ${rev} > /var/lib/clawd/deploy/last-switch.rev; test \"$(cat /run/current-system/configurationRevision 2> /dev/null || true)\" = \"${rev}\"'"
done

View File

@ -49,11 +49,10 @@ sed -i.bak "s/SYNC_TIME/$(date -u +%Y-%m-%dT%H:%M:%SZ)/" "$issues_tmp" && rm -f
# Iterate repos
for repo in $repos; do
repo_name="${repo#*/}"
log "Processing $repo..."
# Fetch open PRs (raw data, no filtering)
prs_json=$(gh pr list -R "$repo" --state open --json number,title,author,createdAt,updatedAt,reviewDecision,labels,isDraft,mergeable,headRefName,url --limit 200 2>/dev/null || echo "[]")
prs_json=$(gh pr list -R "$repo" --state open --json number,title,author,createdAt,updatedAt,reviewDecision,labels,isDraft,mergeable,headRefName,url --limit 200 2> /dev/null || echo "[]")
pr_count=$(echo "$prs_json" | jq 'length')
if [ "$pr_count" -gt 0 ]; then
@ -64,7 +63,7 @@ for repo in $repos; do
fi
# Fetch open issues (excludes PRs)
issues_json=$(gh issue list -R "$repo" --state open --json number,title,author,createdAt,updatedAt,labels,comments,url --limit 200 2>/dev/null || echo "[]")
issues_json=$(gh issue list -R "$repo" --state open --json number,title,author,createdAt,updatedAt,labels,comments,url --limit 200 2> /dev/null || echo "[]")
issue_count=$(echo "$issues_json" | jq 'length')
if [ "$issue_count" -gt 0 ]; then
@ -76,7 +75,7 @@ for repo in $repos; do
done
# Atomic move to final location (use memory-write if available)
if command -v memory-write &>/dev/null; then
if command -v memory-write &> /dev/null; then
memory-write "$GITHUB_DIR/prs.md" < "$prs_tmp"
memory-write "$GITHUB_DIR/issues.md" < "$issues_tmp"
else

View File

@ -12,7 +12,7 @@ if [ -z "${format}" ]; then
ext="${key##*.}"
ext="$(printf '%s' "${ext}" | tr '[:upper:]' '[:lower:]')"
case "${ext}" in
img|raw)
img | raw)
format="raw"
;;
vhd)
@ -92,7 +92,7 @@ for _ in {1..120}; do
echo "${image_id}"
exit 0
;;
deleted|deleting|error)
deleted | deleting | error)
message="$(aws ec2 describe-import-snapshot-tasks \
--region "${region}" \
--import-task-ids "${task_id}" \

View File

@ -9,7 +9,7 @@ mkdir -p "$root/daily" "$root/discord"
index="$root/index.md"
if [ ! -f "$index" ]; then
cat > "$index" <<'EOM'
cat > "$index" << 'EOM'
# Shared Memory Index
- Daily notes live in /memory/daily/YYYY-MM-DD.md

20
scripts/lint-shell.sh Executable file
View File

@ -0,0 +1,20 @@
#!/usr/bin/env bash
set -euo pipefail
# Lint/format gate for repo shell scripts.
# - shellcheck: static analysis
# - shfmt: formatting
# Find shell scripts we own (keep it explicit/simple).
mapfile -t files < <(find scripts -type f -name '*.sh' -print | sort)
if [ "${#files[@]}" -eq 0 ]; then
echo "no shell scripts found" >&2
exit 0
fi
echo "shellcheck (${#files[@]} files)" >&2
shellcheck -S warning "${files[@]}"
echo "shfmt check" >&2
shfmt -i 2 -ci -sr -d "${files[@]}"

View File

@ -31,7 +31,7 @@ if [ ! -f "$stamp_file" ]; then
touch -t 197001010000 "$stamp_file"
fi
exec 9>"$lock_file"
exec 9> "$lock_file"
if ! flock -n 9; then
# Another run is in progress.
exit 0