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:
parent
c44d54319e
commit
52f5168cd2
26
.github/workflows/shell-lint.yml
vendored
Normal file
26
.github/workflows/shell-lint.yml
vendored
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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}"
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}" \
|
||||
|
||||
@ -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
20
scripts/lint-shell.sh
Executable 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[@]}"
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user