From ba5e500ba3722309182be0b673f58c6775a0fda5 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 02:48:41 +0000 Subject: [PATCH 1/7] build(lint): add surface linter config and script Add yamllint, shellcheck, and actionlint configuration plus a scripts/lint-surface.sh entrypoint that lints the repo's YAML files, shell scripts, and GitHub Actions workflows. - .yamllint.yml extends the default profile, disables document-start, downgrades line-length to warning, relaxes truthy/comments for GitHub Actions, and ignores the .agents subtree and generated api/specs. - .shellcheckrc disables SC1091 and intentionally sets no global shell= directive so mixed shebangs (sh/bash) are honored. - .github/actionlint.yml registers the ubuntu-latest-8c custom runner label used by release.yml. - scripts/lint-surface.sh excludes the .agents subtree, jinja template partials, and generated scripts/install output from shellcheck. https://claude.ai/code/session_019trxhNfR8bd4qN7bjjVKoe --- .github/actionlint.yml | 7 +++++++ .shellcheckrc | 8 ++++++++ .yamllint.yml | 22 ++++++++++++++++++++++ scripts/lint-surface.sh | 38 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 75 insertions(+) create mode 100644 .github/actionlint.yml create mode 100644 .shellcheckrc create mode 100644 .yamllint.yml create mode 100755 scripts/lint-surface.sh diff --git a/.github/actionlint.yml b/.github/actionlint.yml new file mode 100644 index 00000000..120b667d --- /dev/null +++ b/.github/actionlint.yml @@ -0,0 +1,7 @@ +# See: https://github.com/rhysd/actionlint/blob/main/docs/config.md +# Register custom org-provided runner labels so actionlint does not flag them +# as unknown. `ubuntu-latest-8c` is a larger GitHub-hosted runner used by the +# release workflow. +self-hosted-runner: + labels: + - ubuntu-latest-8c diff --git a/.shellcheckrc b/.shellcheckrc new file mode 100644 index 00000000..478ea351 --- /dev/null +++ b/.shellcheckrc @@ -0,0 +1,8 @@ +# Do not set a global `shell=` directive: the agent repo has mixed shebangs +# (#!/bin/sh, #!/usr/bin/env bash, #!/bin/bash). Let shellcheck honor each +# file's shebang so bash scripts (scripts/preflight.sh, api/regen.sh) are not +# falsely flagged with SC3040 (pipefail undefined in POSIX sh). + +# SC1091: Not following sourced files. Several scripts source files shellcheck +# cannot resolve (.venv/bin/activate, $HOME/.cargo/env, build/git-tags.sh). +disable=SC1091 diff --git a/.yamllint.yml b/.yamllint.yml new file mode 100644 index 00000000..27e2a52a --- /dev/null +++ b/.yamllint.yml @@ -0,0 +1,22 @@ +# See: https://yamllint.readthedocs.io/en/stable/ + +extends: default + +rules: + document-start: + present: false + line-length: + level: warning + allow-non-breakable-inline-mappings: true + # GitHub Actions uses `on:` as a workflow trigger key; YAML 1.1 treats `on` + # as a truthy boolean alias. Disable key checking to avoid false positives. + truthy: + check-keys: false + # SHA-pinned action comments use a single space before `#`. Reduce the + # minimum from the default 2 to 1 to match the existing convention. + comments: + min-spaces-from-content: 1 + +ignore: | + .agents/ + api/specs/ diff --git a/scripts/lint-surface.sh b/scripts/lint-surface.sh new file mode 100755 index 00000000..50491d64 --- /dev/null +++ b/scripts/lint-surface.sh @@ -0,0 +1,38 @@ +#!/bin/sh +set -e + +command -v yamllint >/dev/null 2>&1 || { echo "error: yamllint not installed" >&2; exit 1; } +command -v shellcheck >/dev/null 2>&1 || { echo "error: shellcheck not installed" >&2; exit 1; } +command -v actionlint >/dev/null 2>&1 || { echo "error: actionlint not installed" >&2; exit 1; } + +REPO_ROOT=$(git rev-parse --show-toplevel) +cd "$REPO_ROOT" + +echo "yamllint" +echo "--------" +yamllint -c .yamllint.yml . +echo "" + +echo "shellcheck" +echo "----------" +# Exclude: +# - the .agents subtree (content owned by another repo); +# - the jinja template fragments under scripts/jinja/templates/ (shebang-less +# .sh partials concatenated by scripts/jinja/render.sh; shellcheck flags +# them SC2148 on their own); +# - the generated install scripts under scripts/install/ (rendered from the +# jinja templates by scripts/jinja/render.py — they are build artifacts, so +# findings must be fixed in the templates, not the generated output). +find . -name '*.sh' \ + ! -path './.agents/*' \ + ! -path './scripts/jinja/templates/*' \ + ! -path './scripts/install/*' \ + -exec shellcheck {} + +echo "" + +echo "actionlint" +echo "----------" +actionlint +echo "" + +echo "Surface lint complete" From add589f205002c3533f7d2fcb0ab61aac22acdd9 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 02:48:41 +0000 Subject: [PATCH 2/7] style(surface): fix yaml and shell findings flagged by surface lint Resolve the error-level findings the new surface linter reports: - builder.yml / .goreleaser.yaml: remove trailing blank lines. - .goreleaser.yaml / install.yaml: strip trailing spaces; add the missing final newline to install.yaml. - render.sh: drop the unused BLUE color variable (SC2034). - covgate.sh / update-covgates.sh: quote $SRC_DIR inside the parameter expansion (SC2295). https://claude.ai/code/session_019trxhNfR8bd4qN7bjjVKoe --- .github/workflows/builder.yml | 1 - build/.goreleaser.yaml | 5 ++--- scripts/jinja/install.yaml | 26 +++++++++++++------------- scripts/jinja/render.sh | 1 - scripts/lib/covgate.sh | 2 +- scripts/lib/update-covgates.sh | 2 +- 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/.github/workflows/builder.yml b/.github/workflows/builder.yml index 5320030f..9d5adafb 100644 --- a/.github/workflows/builder.yml +++ b/.github/workflows/builder.yml @@ -55,4 +55,3 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max - diff --git a/build/.goreleaser.yaml b/build/.goreleaser.yaml index 3d769850..45fda0e2 100644 --- a/build/.goreleaser.yaml +++ b/build/.goreleaser.yaml @@ -30,7 +30,7 @@ nfpms: description: | Miru Agent. Miru provides the infrastructure to version, manage, and deploy application configurations at scale. This debian package is the miru agent, which handles configuration deployment to your robots in production. - + formats: - deb @@ -126,7 +126,7 @@ release: ## Miru Agent {{.Tag}} **Full Changelog**: https://github.com/mirurobotics/agent/compare/{{ .PreviousTag }}...{{ .Tag }} - + For documentation, visit: https://docs.mirurobotics.com @@ -134,4 +134,3 @@ source: enabled: false report_sizes: true - diff --git a/scripts/jinja/install.yaml b/scripts/jinja/install.yaml index 2793ed98..e9d25fc2 100644 --- a/scripts/jinja/install.yaml +++ b/scripts/jinja/install.yaml @@ -131,7 +131,7 @@ base_install_vars: &base_install_vars debian_pkg_mime_type: *debian_pkg_mime_type prod_install: &prod_install - args: + args: <<: *base_install_args backend_host: *prod_backend_host mqtt_broker_host: *prod_mqtt_broker_host @@ -140,7 +140,7 @@ prod_install: &prod_install utilities: *checksum_utility staging_install: &staging_install - args: + args: <<: *base_install_args backend_host: *staging_backend_host mqtt_broker_host: *staging_mqtt_broker_host @@ -149,7 +149,7 @@ staging_install: &staging_install utilities: *checksum_utility uat_install: &uat_install - args: + args: <<: *base_install_args backend_host: *uat_backend_host mqtt_broker_host: *uat_mqtt_broker_host @@ -175,7 +175,7 @@ base_provision_vars: &base_provision_vars debian_pkg_mime_type: *debian_pkg_mime_type prod_provision: &prod_provision - args: + args: <<: *base_provision_args backend_host: *prod_backend_host mqtt_broker_host: *prod_mqtt_broker_host @@ -184,7 +184,7 @@ prod_provision: &prod_provision utilities: *checksum_utility staging_provision: &staging_provision - args: + args: <<: *base_provision_args backend_host: *staging_backend_host mqtt_broker_host: *staging_mqtt_broker_host @@ -193,7 +193,7 @@ staging_provision: &staging_provision utilities: *checksum_utility uat_provision: &uat_provision - args: + args: <<: *base_provision_args backend_host: *uat_backend_host mqtt_broker_host: *uat_mqtt_broker_host @@ -206,31 +206,31 @@ uat_provision: &uat_provision scripts: # installs - name: "install.sh" - template: "install.j2" + template: "install.j2" description: "Install the Miru Agent" variables: *prod_install - name: "staging-install.sh" - template: "install.j2" + template: "install.j2" description: "Install the Miru Agent in the staging environment" variables: *staging_install - name: "uat-install.sh" - template: "install.j2" + template: "install.j2" description: "Install the Miru Agent in the UAT environment" variables: *uat_install # provisions - name: "provision.sh" - template: "provision.j2" + template: "provision.j2" description: "Provision a device & install the Miru Agent" variables: <<: *prod_provision - name: "staging-provision.sh" - template: "provision.j2" + template: "provision.j2" description: "Provision a device & install the Miru Agent in the staging environment" variables: <<: *staging_provision - name: "uat-provision.sh" - template: "provision.j2" + template: "provision.j2" description: "Provision a device & install the Miru Agent in the UAT environment" variables: - <<: *uat_provision \ No newline at end of file + <<: *uat_provision diff --git a/scripts/jinja/render.sh b/scripts/jinja/render.sh index 68f7dbec..916987e7 100755 --- a/scripts/jinja/render.sh +++ b/scripts/jinja/render.sh @@ -10,7 +10,6 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' -BLUE='\033[0;34m' NC='\033[0m' log() { echo "${GREEN}==>${NC} $1"; } diff --git a/scripts/lib/covgate.sh b/scripts/lib/covgate.sh index d5ecbeb7..ed88bb1b 100755 --- a/scripts/lib/covgate.sh +++ b/scripts/lib/covgate.sh @@ -71,7 +71,7 @@ find "$SRC_DIR" -name '.covgate' -type f | sort > "$covgate_list" while read -r covgate_file; do module_path=$(dirname "$covgate_file") - module_display="${module_path#$SRC_DIR/}" + module_display="${module_path#"$SRC_DIR"/}" # If .covgate is directly in SRC_DIR, display as the directory name if [ "$module_display" = "$module_path" ]; then module_display=$(basename "$SRC_DIR") diff --git a/scripts/lib/update-covgates.sh b/scripts/lib/update-covgates.sh index 42db3630..3ce32b2d 100755 --- a/scripts/lib/update-covgates.sh +++ b/scripts/lib/update-covgates.sh @@ -66,7 +66,7 @@ find "$SRC_DIR" -name '.covgate' -type f | sort > "$covgate_list" while read -r covgate_file; do module_path=$(dirname "$covgate_file") - module_display="${module_path#$SRC_DIR/}" + module_display="${module_path#"$SRC_DIR"/}" # If .covgate is directly in SRC_DIR, display as the directory name if [ "$module_display" = "$module_path" ]; then module_display=$(basename "$SRC_DIR") From d7dc24b844b50c66a6f569e69ff6e658700c6a32 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 02:48:41 +0000 Subject: [PATCH 3/7] build(lint): wire surface lint into lint.sh and dep installer Chain scripts/lint-surface.sh after the Rust lint in scripts/lint.sh so a local lint run (and therefore scripts/preflight.sh) exercises it, and add idempotent yamllint/shellcheck/actionlint installs to scripts/lib/install-lint-deps.sh. https://claude.ai/code/session_019trxhNfR8bd4qN7bjjVKoe --- scripts/lib/install-lint-deps.sh | 21 +++++++++++++++++++++ scripts/lint.sh | 4 +++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/scripts/lib/install-lint-deps.sh b/scripts/lib/install-lint-deps.sh index eb21e61d..b30488a3 100755 --- a/scripts/lib/install-lint-deps.sh +++ b/scripts/lib/install-lint-deps.sh @@ -75,3 +75,24 @@ if ! command_exists cargo-diet && ! has_cargo_subcommand cargo-diet; then echo "---------------------" install_cargo_tool cargo-diet fi + +# Surface lint tools: yamllint, shellcheck, actionlint. These power +# scripts/lint-surface.sh (YAML / shell / GitHub Actions linting). Each install +# is guarded by command_exists so re-running this script is idempotent. +if ! command_exists yamllint; then + echo "Installing yamllint" + echo "-------------------" + pip install --user yamllint || pipx install yamllint +fi +if ! command_exists shellcheck; then + echo "Installing shellcheck" + echo "---------------------" + # Debian/Ubuntu; adjust per platform if needed. + sudo apt-get install -y shellcheck +fi +if ! command_exists actionlint; then + echo "Installing actionlint" + echo "---------------------" + go install github.com/rhysd/actionlint/cmd/actionlint@latest \ + || curl -sSfL https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash | bash +fi diff --git a/scripts/lint.sh b/scripts/lint.sh index 9df6b6c4..2f8f3f22 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -10,4 +10,6 @@ export ASSERT_LINT_PATHS="$REPO_ROOT/agent/tests" export IMPORT_LINT_CONFIG="$REPO_ROOT/.lint-imports.toml" export RUN_DIET="1" -exec "$REPO_ROOT/scripts/lib/lint.sh" +"$REPO_ROOT/scripts/lib/lint.sh" + +"$REPO_ROOT/scripts/lint-surface.sh" From 8286cb18e37797b4cb3be6de8c9adf8f6d93addc Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 02:48:41 +0000 Subject: [PATCH 4/7] ci(agent): add surface-lint job delegating to shared workflow Add a surface-lint job that calls the shared reusable workflow mirurobotics/.github/.github/workflows/surface-lint.yml@main on ubuntu-latest, enforcing YAML / shell / GitHub Actions linting in CI. https://claude.ai/code/session_019trxhNfR8bd4qN7bjjVKoe --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9557de4f..cd52fbfa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -85,3 +85,9 @@ jobs: - name: Run Tests + Coverage Gate run: ./tools/lint/scripts/covgate.sh + + surface-lint: + # yamllint disable rule:line-length + uses: mirurobotics/.github/.github/workflows/surface-lint.yml@main + with: + runs-on: ubuntu-latest From 1311e892d59cc1a3a62ac700e7e4d7564e4b7283 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 19:06:18 +0000 Subject: [PATCH 5/7] style(install): make jinja templates and generated install scripts pass shellcheck Add shellcheck directives to the shebang-less jinja partials (shell=sh, plus narrow per-line/per-function disables for trap/logging-API false positives and cross-partial variable use), quote exit "$exit_code" in the cleanup trap, and keep the intentional $args word-splitting with a justified SC2086 disable. Regenerate scripts/install/*.sh from the templates and drop the jinja/install exclusions from lint-surface.sh so it lints the same surface as the shared CI workflow. https://claude.ai/code/session_019trxhNfR8bd4qN7bjjVKoe --- scripts/install/install.sh | 19 +++++++++++++--- scripts/install/provision.sh | 22 ++++++++++++++++--- scripts/install/staging-install.sh | 19 +++++++++++++--- scripts/install/staging-provision.sh | 22 ++++++++++++++++--- scripts/install/uat-install.sh | 19 +++++++++++++--- scripts/install/uat-provision.sh | 22 ++++++++++++++++--- scripts/jinja/templates/partials/display.sh | 3 +++ .../templates/partials/utils/activate.sh | 5 ++++- .../templates/partials/utils/checksum.sh | 1 + .../templates/partials/utils/download.sh | 4 +++- .../partials/utils/parse-from-pkg.sh | 3 +++ .../templates/partials/utils/provision.sh | 3 +++ .../jinja/templates/partials/utils/version.sh | 1 + scripts/lint-surface.sh | 16 +++++--------- 14 files changed, 129 insertions(+), 30 deletions(-) diff --git a/scripts/install/install.sh b/scripts/install/install.sh index 3d459b00..b3c508d0 100755 --- a/scripts/install/install.sh +++ b/scripts/install/install.sh @@ -3,11 +3,12 @@ set -e # Script: install.sh # Jinja Template: install.j2 -# Build Timestamp: 2026-05-09T19:53:58.827613 +# Build Timestamp: 2026-06-03T19:03:43.199612 # Description: Install the Miru Agent # DISPLAY # # ======= # +# shellcheck shell=sh RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -16,7 +17,9 @@ NO_COLOR='\033[0m' debug() { echo "${BLUE}==>${NO_COLOR} $1"; } log() { echo "${GREEN}==>${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper warn() { echo "${YELLOW}Warning:${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper error() { echo "${RED}Error:${NO_COLOR} $1"; } fatal() { echo "${RED}Error:${NO_COLOR} $1"; exit 1; } @@ -102,6 +105,7 @@ for cmd in curl grep cut jq; do done +# shellcheck shell=sh verify_checksum() { file=$1 expected_checksum=$2 @@ -152,6 +156,7 @@ esac # USE PROVIDED PACKAGE # # -------------------- # +# shellcheck shell=sh if [ -n "$FROM_PKG" ]; then log "Installing from package on local machine: '$FROM_PKG'" if [ ! -f "$FROM_PKG" ]; then @@ -166,13 +171,16 @@ if [ -n "$FROM_PKG" ]; then if [ "$(dpkg -f "$FROM_PKG" Architecture)" != "$DEB_ARCH" ]; then fatal "The provided package architecture ($(dpkg -f "$FROM_PKG" Architecture)) does not match this machine's architecture ($DEB_ARCH)." fi + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script AGENT_DEB_PKG=$FROM_PKG + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script VERSION=$(dpkg -f "$FROM_PKG" Version) fi # DETERMINE THE VERSION # # --------------------- # +# shellcheck shell=sh if [ -z "$VERSION" ]; then if [ "$PRERELEASE" = true ]; then log "Fetching latest pre-release version..." @@ -208,9 +216,11 @@ fi # DOWNLOAD THE AGENT # # ------------------ # +# shellcheck shell=sh INSTALLED_VERSION=$(dpkg-query -W -f='${Version}' "$AGENT_DEB_PKG_NAME" 2>/dev/null || echo "") -# replace '~' with '-' +# replace '~' with '-' if [ -n "$INSTALLED_VERSION" ]; then + # shellcheck disable=SC2001 # POSIX sh lacks ${var//search/replace}; sed is required INSTALLED_VERSION=$(echo "$INSTALLED_VERSION" | sed 's/~/-/g') fi @@ -254,6 +264,8 @@ fi # ACTIVATE THE AGENT # # ------------------ # +# shellcheck shell=sh +# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? @@ -261,7 +273,7 @@ cleanup() { log "Restarting the Miru Agent" sudo systemctl restart miru >/dev/null 2>&1 - exit $exit_code + exit "$exit_code" } trap cleanup EXIT INT TERM QUIT HUP @@ -288,5 +300,6 @@ fi sudo chown -R miru:miru /srv/miru # Execute the installer +# shellcheck disable=SC2086 # word-splitting of the argument list is intentional sudo -u miru -E env MIRU_ACTIVATION_TOKEN="$MIRU_ACTIVATION_TOKEN" /usr/sbin/miru-agent --install $args exit 0 \ No newline at end of file diff --git a/scripts/install/provision.sh b/scripts/install/provision.sh index d01c379d..0da6e879 100755 --- a/scripts/install/provision.sh +++ b/scripts/install/provision.sh @@ -3,11 +3,12 @@ set -e # Script: provision.sh # Jinja Template: provision.j2 -# Build Timestamp: 2026-05-09T19:53:58.827613 +# Build Timestamp: 2026-06-03T19:03:43.199612 # Description: Provision a device & install the Miru Agent # DISPLAY # # ======= # +# shellcheck shell=sh RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -16,7 +17,9 @@ NO_COLOR='\033[0m' debug() { echo "${BLUE}==>${NO_COLOR} $1"; } log() { echo "${GREEN}==>${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper warn() { echo "${YELLOW}Warning:${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper error() { echo "${RED}Error:${NO_COLOR} $1"; } fatal() { echo "${RED}Error:${NO_COLOR} $1"; exit 1; } @@ -116,6 +119,7 @@ for cmd in curl grep cut jq; do done +# shellcheck shell=sh verify_checksum() { file=$1 expected_checksum=$2 @@ -166,6 +170,7 @@ esac # USE PROVIDED PACKAGE # # -------------------- # +# shellcheck shell=sh if [ -n "$FROM_PKG" ]; then log "Installing from package on local machine: '$FROM_PKG'" if [ ! -f "$FROM_PKG" ]; then @@ -180,18 +185,22 @@ if [ -n "$FROM_PKG" ]; then if [ "$(dpkg -f "$FROM_PKG" Architecture)" != "$DEB_ARCH" ]; then fatal "The provided package architecture ($(dpkg -f "$FROM_PKG" Architecture)) does not match this machine's architecture ($DEB_ARCH)." fi + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script AGENT_DEB_PKG=$FROM_PKG + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script VERSION=$(dpkg -f "$FROM_PKG" Version) fi # PROVISION THE DEVICE # # --------------------- # +# shellcheck shell=sh if [ -z "$MIRU_API_KEY" ]; then echo "MIRU_API_KEY is not set" exit 1 fi +# shellcheck disable=SC2153 # DEVICE_NAME is an external environment variable response_body=$(curl --request POST \ --url "$BACKEND_HOST"/v1/devices \ --header 'Content-Type: application/json' \ @@ -261,6 +270,7 @@ response_body=$(echo "$response_body" | head -n -1) # Check if the request succeeded if [ "$http_code" -eq 200 ] || [ "$http_code" -eq 201 ]; then log "Successfully created activation token" + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script MIRU_ACTIVATION_TOKEN=$(echo "$response_body" | jq -r '.token') else error "Activation token request failed (HTTP status $http_code)" @@ -270,6 +280,7 @@ fi # DETERMINE THE VERSION # # --------------------- # +# shellcheck shell=sh if [ -z "$VERSION" ]; then if [ "$PRERELEASE" = true ]; then log "Fetching latest pre-release version..." @@ -305,9 +316,11 @@ fi # DOWNLOAD THE AGENT # # ------------------ # +# shellcheck shell=sh INSTALLED_VERSION=$(dpkg-query -W -f='${Version}' "$AGENT_DEB_PKG_NAME" 2>/dev/null || echo "") -# replace '~' with '-' +# replace '~' with '-' if [ -n "$INSTALLED_VERSION" ]; then + # shellcheck disable=SC2001 # POSIX sh lacks ${var//search/replace}; sed is required INSTALLED_VERSION=$(echo "$INSTALLED_VERSION" | sed 's/~/-/g') fi @@ -351,6 +364,8 @@ fi # ACTIVATE THE AGENT # # ------------------ # +# shellcheck shell=sh +# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? @@ -358,7 +373,7 @@ cleanup() { log "Restarting the Miru Agent" sudo systemctl restart miru >/dev/null 2>&1 - exit $exit_code + exit "$exit_code" } trap cleanup EXIT INT TERM QUIT HUP @@ -385,5 +400,6 @@ fi sudo chown -R miru:miru /srv/miru # Execute the installer +# shellcheck disable=SC2086 # word-splitting of the argument list is intentional sudo -u miru -E env MIRU_ACTIVATION_TOKEN="$MIRU_ACTIVATION_TOKEN" /usr/sbin/miru-agent --install $args exit 0 \ No newline at end of file diff --git a/scripts/install/staging-install.sh b/scripts/install/staging-install.sh index fe76697d..5fb04d5b 100755 --- a/scripts/install/staging-install.sh +++ b/scripts/install/staging-install.sh @@ -3,11 +3,12 @@ set -e # Script: staging-install.sh # Jinja Template: install.j2 -# Build Timestamp: 2026-05-09T19:53:58.827613 +# Build Timestamp: 2026-06-03T19:03:43.199612 # Description: Install the Miru Agent in the staging environment # DISPLAY # # ======= # +# shellcheck shell=sh RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -16,7 +17,9 @@ NO_COLOR='\033[0m' debug() { echo "${BLUE}==>${NO_COLOR} $1"; } log() { echo "${GREEN}==>${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper warn() { echo "${YELLOW}Warning:${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper error() { echo "${RED}Error:${NO_COLOR} $1"; } fatal() { echo "${RED}Error:${NO_COLOR} $1"; exit 1; } @@ -102,6 +105,7 @@ for cmd in curl grep cut jq; do done +# shellcheck shell=sh verify_checksum() { file=$1 expected_checksum=$2 @@ -152,6 +156,7 @@ esac # USE PROVIDED PACKAGE # # -------------------- # +# shellcheck shell=sh if [ -n "$FROM_PKG" ]; then log "Installing from package on local machine: '$FROM_PKG'" if [ ! -f "$FROM_PKG" ]; then @@ -166,13 +171,16 @@ if [ -n "$FROM_PKG" ]; then if [ "$(dpkg -f "$FROM_PKG" Architecture)" != "$DEB_ARCH" ]; then fatal "The provided package architecture ($(dpkg -f "$FROM_PKG" Architecture)) does not match this machine's architecture ($DEB_ARCH)." fi + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script AGENT_DEB_PKG=$FROM_PKG + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script VERSION=$(dpkg -f "$FROM_PKG" Version) fi # DETERMINE THE VERSION # # --------------------- # +# shellcheck shell=sh if [ -z "$VERSION" ]; then if [ "$PRERELEASE" = true ]; then log "Fetching latest pre-release version..." @@ -208,9 +216,11 @@ fi # DOWNLOAD THE AGENT # # ------------------ # +# shellcheck shell=sh INSTALLED_VERSION=$(dpkg-query -W -f='${Version}' "$AGENT_DEB_PKG_NAME" 2>/dev/null || echo "") -# replace '~' with '-' +# replace '~' with '-' if [ -n "$INSTALLED_VERSION" ]; then + # shellcheck disable=SC2001 # POSIX sh lacks ${var//search/replace}; sed is required INSTALLED_VERSION=$(echo "$INSTALLED_VERSION" | sed 's/~/-/g') fi @@ -254,6 +264,8 @@ fi # ACTIVATE THE AGENT # # ------------------ # +# shellcheck shell=sh +# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? @@ -261,7 +273,7 @@ cleanup() { log "Restarting the Miru Agent" sudo systemctl restart miru >/dev/null 2>&1 - exit $exit_code + exit "$exit_code" } trap cleanup EXIT INT TERM QUIT HUP @@ -288,5 +300,6 @@ fi sudo chown -R miru:miru /srv/miru # Execute the installer +# shellcheck disable=SC2086 # word-splitting of the argument list is intentional sudo -u miru -E env MIRU_ACTIVATION_TOKEN="$MIRU_ACTIVATION_TOKEN" /usr/sbin/miru-agent --install $args exit 0 \ No newline at end of file diff --git a/scripts/install/staging-provision.sh b/scripts/install/staging-provision.sh index 36bc2f7d..52489b0b 100755 --- a/scripts/install/staging-provision.sh +++ b/scripts/install/staging-provision.sh @@ -3,11 +3,12 @@ set -e # Script: staging-provision.sh # Jinja Template: provision.j2 -# Build Timestamp: 2026-05-09T19:53:58.827613 +# Build Timestamp: 2026-06-03T19:03:43.199612 # Description: Provision a device & install the Miru Agent in the staging environment # DISPLAY # # ======= # +# shellcheck shell=sh RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -16,7 +17,9 @@ NO_COLOR='\033[0m' debug() { echo "${BLUE}==>${NO_COLOR} $1"; } log() { echo "${GREEN}==>${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper warn() { echo "${YELLOW}Warning:${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper error() { echo "${RED}Error:${NO_COLOR} $1"; } fatal() { echo "${RED}Error:${NO_COLOR} $1"; exit 1; } @@ -116,6 +119,7 @@ for cmd in curl grep cut jq; do done +# shellcheck shell=sh verify_checksum() { file=$1 expected_checksum=$2 @@ -166,6 +170,7 @@ esac # USE PROVIDED PACKAGE # # -------------------- # +# shellcheck shell=sh if [ -n "$FROM_PKG" ]; then log "Installing from package on local machine: '$FROM_PKG'" if [ ! -f "$FROM_PKG" ]; then @@ -180,18 +185,22 @@ if [ -n "$FROM_PKG" ]; then if [ "$(dpkg -f "$FROM_PKG" Architecture)" != "$DEB_ARCH" ]; then fatal "The provided package architecture ($(dpkg -f "$FROM_PKG" Architecture)) does not match this machine's architecture ($DEB_ARCH)." fi + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script AGENT_DEB_PKG=$FROM_PKG + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script VERSION=$(dpkg -f "$FROM_PKG" Version) fi # PROVISION THE DEVICE # # --------------------- # +# shellcheck shell=sh if [ -z "$MIRU_API_KEY" ]; then echo "MIRU_API_KEY is not set" exit 1 fi +# shellcheck disable=SC2153 # DEVICE_NAME is an external environment variable response_body=$(curl --request POST \ --url "$BACKEND_HOST"/v1/devices \ --header 'Content-Type: application/json' \ @@ -261,6 +270,7 @@ response_body=$(echo "$response_body" | head -n -1) # Check if the request succeeded if [ "$http_code" -eq 200 ] || [ "$http_code" -eq 201 ]; then log "Successfully created activation token" + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script MIRU_ACTIVATION_TOKEN=$(echo "$response_body" | jq -r '.token') else error "Activation token request failed (HTTP status $http_code)" @@ -270,6 +280,7 @@ fi # DETERMINE THE VERSION # # --------------------- # +# shellcheck shell=sh if [ -z "$VERSION" ]; then if [ "$PRERELEASE" = true ]; then log "Fetching latest pre-release version..." @@ -305,9 +316,11 @@ fi # DOWNLOAD THE AGENT # # ------------------ # +# shellcheck shell=sh INSTALLED_VERSION=$(dpkg-query -W -f='${Version}' "$AGENT_DEB_PKG_NAME" 2>/dev/null || echo "") -# replace '~' with '-' +# replace '~' with '-' if [ -n "$INSTALLED_VERSION" ]; then + # shellcheck disable=SC2001 # POSIX sh lacks ${var//search/replace}; sed is required INSTALLED_VERSION=$(echo "$INSTALLED_VERSION" | sed 's/~/-/g') fi @@ -351,6 +364,8 @@ fi # ACTIVATE THE AGENT # # ------------------ # +# shellcheck shell=sh +# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? @@ -358,7 +373,7 @@ cleanup() { log "Restarting the Miru Agent" sudo systemctl restart miru >/dev/null 2>&1 - exit $exit_code + exit "$exit_code" } trap cleanup EXIT INT TERM QUIT HUP @@ -385,5 +400,6 @@ fi sudo chown -R miru:miru /srv/miru # Execute the installer +# shellcheck disable=SC2086 # word-splitting of the argument list is intentional sudo -u miru -E env MIRU_ACTIVATION_TOKEN="$MIRU_ACTIVATION_TOKEN" /usr/sbin/miru-agent --install $args exit 0 \ No newline at end of file diff --git a/scripts/install/uat-install.sh b/scripts/install/uat-install.sh index a00c1a02..13f14eb1 100755 --- a/scripts/install/uat-install.sh +++ b/scripts/install/uat-install.sh @@ -3,11 +3,12 @@ set -e # Script: uat-install.sh # Jinja Template: install.j2 -# Build Timestamp: 2026-05-09T19:53:58.827613 +# Build Timestamp: 2026-06-03T19:03:43.199612 # Description: Install the Miru Agent in the UAT environment # DISPLAY # # ======= # +# shellcheck shell=sh RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -16,7 +17,9 @@ NO_COLOR='\033[0m' debug() { echo "${BLUE}==>${NO_COLOR} $1"; } log() { echo "${GREEN}==>${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper warn() { echo "${YELLOW}Warning:${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper error() { echo "${RED}Error:${NO_COLOR} $1"; } fatal() { echo "${RED}Error:${NO_COLOR} $1"; exit 1; } @@ -102,6 +105,7 @@ for cmd in curl grep cut jq; do done +# shellcheck shell=sh verify_checksum() { file=$1 expected_checksum=$2 @@ -152,6 +156,7 @@ esac # USE PROVIDED PACKAGE # # -------------------- # +# shellcheck shell=sh if [ -n "$FROM_PKG" ]; then log "Installing from package on local machine: '$FROM_PKG'" if [ ! -f "$FROM_PKG" ]; then @@ -166,13 +171,16 @@ if [ -n "$FROM_PKG" ]; then if [ "$(dpkg -f "$FROM_PKG" Architecture)" != "$DEB_ARCH" ]; then fatal "The provided package architecture ($(dpkg -f "$FROM_PKG" Architecture)) does not match this machine's architecture ($DEB_ARCH)." fi + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script AGENT_DEB_PKG=$FROM_PKG + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script VERSION=$(dpkg -f "$FROM_PKG" Version) fi # DETERMINE THE VERSION # # --------------------- # +# shellcheck shell=sh if [ -z "$VERSION" ]; then if [ "$PRERELEASE" = true ]; then log "Fetching latest pre-release version..." @@ -208,9 +216,11 @@ fi # DOWNLOAD THE AGENT # # ------------------ # +# shellcheck shell=sh INSTALLED_VERSION=$(dpkg-query -W -f='${Version}' "$AGENT_DEB_PKG_NAME" 2>/dev/null || echo "") -# replace '~' with '-' +# replace '~' with '-' if [ -n "$INSTALLED_VERSION" ]; then + # shellcheck disable=SC2001 # POSIX sh lacks ${var//search/replace}; sed is required INSTALLED_VERSION=$(echo "$INSTALLED_VERSION" | sed 's/~/-/g') fi @@ -254,6 +264,8 @@ fi # ACTIVATE THE AGENT # # ------------------ # +# shellcheck shell=sh +# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? @@ -261,7 +273,7 @@ cleanup() { log "Restarting the Miru Agent" sudo systemctl restart miru >/dev/null 2>&1 - exit $exit_code + exit "$exit_code" } trap cleanup EXIT INT TERM QUIT HUP @@ -288,5 +300,6 @@ fi sudo chown -R miru:miru /srv/miru # Execute the installer +# shellcheck disable=SC2086 # word-splitting of the argument list is intentional sudo -u miru -E env MIRU_ACTIVATION_TOKEN="$MIRU_ACTIVATION_TOKEN" /usr/sbin/miru-agent --install $args exit 0 \ No newline at end of file diff --git a/scripts/install/uat-provision.sh b/scripts/install/uat-provision.sh index 379b0ba5..a0024c18 100755 --- a/scripts/install/uat-provision.sh +++ b/scripts/install/uat-provision.sh @@ -3,11 +3,12 @@ set -e # Script: uat-provision.sh # Jinja Template: provision.j2 -# Build Timestamp: 2026-05-09T19:53:58.827613 +# Build Timestamp: 2026-06-03T19:03:43.199612 # Description: Provision a device & install the Miru Agent in the UAT environment # DISPLAY # # ======= # +# shellcheck shell=sh RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -16,7 +17,9 @@ NO_COLOR='\033[0m' debug() { echo "${BLUE}==>${NO_COLOR} $1"; } log() { echo "${GREEN}==>${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper warn() { echo "${YELLOW}Warning:${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper error() { echo "${RED}Error:${NO_COLOR} $1"; } fatal() { echo "${RED}Error:${NO_COLOR} $1"; exit 1; } @@ -116,6 +119,7 @@ for cmd in curl grep cut jq; do done +# shellcheck shell=sh verify_checksum() { file=$1 expected_checksum=$2 @@ -166,6 +170,7 @@ esac # USE PROVIDED PACKAGE # # -------------------- # +# shellcheck shell=sh if [ -n "$FROM_PKG" ]; then log "Installing from package on local machine: '$FROM_PKG'" if [ ! -f "$FROM_PKG" ]; then @@ -180,18 +185,22 @@ if [ -n "$FROM_PKG" ]; then if [ "$(dpkg -f "$FROM_PKG" Architecture)" != "$DEB_ARCH" ]; then fatal "The provided package architecture ($(dpkg -f "$FROM_PKG" Architecture)) does not match this machine's architecture ($DEB_ARCH)." fi + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script AGENT_DEB_PKG=$FROM_PKG + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script VERSION=$(dpkg -f "$FROM_PKG" Version) fi # PROVISION THE DEVICE # # --------------------- # +# shellcheck shell=sh if [ -z "$MIRU_API_KEY" ]; then echo "MIRU_API_KEY is not set" exit 1 fi +# shellcheck disable=SC2153 # DEVICE_NAME is an external environment variable response_body=$(curl --request POST \ --url "$BACKEND_HOST"/v1/devices \ --header 'Content-Type: application/json' \ @@ -261,6 +270,7 @@ response_body=$(echo "$response_body" | head -n -1) # Check if the request succeeded if [ "$http_code" -eq 200 ] || [ "$http_code" -eq 201 ]; then log "Successfully created activation token" + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script MIRU_ACTIVATION_TOKEN=$(echo "$response_body" | jq -r '.token') else error "Activation token request failed (HTTP status $http_code)" @@ -270,6 +280,7 @@ fi # DETERMINE THE VERSION # # --------------------- # +# shellcheck shell=sh if [ -z "$VERSION" ]; then if [ "$PRERELEASE" = true ]; then log "Fetching latest pre-release version..." @@ -305,9 +316,11 @@ fi # DOWNLOAD THE AGENT # # ------------------ # +# shellcheck shell=sh INSTALLED_VERSION=$(dpkg-query -W -f='${Version}' "$AGENT_DEB_PKG_NAME" 2>/dev/null || echo "") -# replace '~' with '-' +# replace '~' with '-' if [ -n "$INSTALLED_VERSION" ]; then + # shellcheck disable=SC2001 # POSIX sh lacks ${var//search/replace}; sed is required INSTALLED_VERSION=$(echo "$INSTALLED_VERSION" | sed 's/~/-/g') fi @@ -351,6 +364,8 @@ fi # ACTIVATE THE AGENT # # ------------------ # +# shellcheck shell=sh +# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? @@ -358,7 +373,7 @@ cleanup() { log "Restarting the Miru Agent" sudo systemctl restart miru >/dev/null 2>&1 - exit $exit_code + exit "$exit_code" } trap cleanup EXIT INT TERM QUIT HUP @@ -385,5 +400,6 @@ fi sudo chown -R miru:miru /srv/miru # Execute the installer +# shellcheck disable=SC2086 # word-splitting of the argument list is intentional sudo -u miru -E env MIRU_ACTIVATION_TOKEN="$MIRU_ACTIVATION_TOKEN" /usr/sbin/miru-agent --install $args exit 0 \ No newline at end of file diff --git a/scripts/jinja/templates/partials/display.sh b/scripts/jinja/templates/partials/display.sh index 12867d9f..be6f0c16 100644 --- a/scripts/jinja/templates/partials/display.sh +++ b/scripts/jinja/templates/partials/display.sh @@ -1,3 +1,4 @@ +# shellcheck shell=sh RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' @@ -6,6 +7,8 @@ NO_COLOR='\033[0m' debug() { echo "${BLUE}==>${NO_COLOR} $1"; } log() { echo "${GREEN}==>${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper warn() { echo "${YELLOW}Warning:${NO_COLOR} $1"; } +# shellcheck disable=SC2317,SC2329 # part of the shared logging API; not every script calls every helper error() { echo "${RED}Error:${NO_COLOR} $1"; } fatal() { echo "${RED}Error:${NO_COLOR} $1"; exit 1; } \ No newline at end of file diff --git a/scripts/jinja/templates/partials/utils/activate.sh b/scripts/jinja/templates/partials/utils/activate.sh index f131644e..b3ca2b19 100644 --- a/scripts/jinja/templates/partials/utils/activate.sh +++ b/scripts/jinja/templates/partials/utils/activate.sh @@ -1,3 +1,5 @@ +# shellcheck shell=sh +# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? @@ -5,7 +7,7 @@ cleanup() { log "Restarting the Miru Agent" sudo systemctl restart miru >/dev/null 2>&1 - exit $exit_code + exit "$exit_code" } trap cleanup EXIT INT TERM QUIT HUP @@ -32,4 +34,5 @@ fi sudo chown -R miru:miru /srv/miru # Execute the installer +# shellcheck disable=SC2086 # word-splitting of the argument list is intentional sudo -u miru -E env MIRU_ACTIVATION_TOKEN="$MIRU_ACTIVATION_TOKEN" /usr/sbin/miru-agent --install $args \ No newline at end of file diff --git a/scripts/jinja/templates/partials/utils/checksum.sh b/scripts/jinja/templates/partials/utils/checksum.sh index 04186fe3..aea28079 100644 --- a/scripts/jinja/templates/partials/utils/checksum.sh +++ b/scripts/jinja/templates/partials/utils/checksum.sh @@ -1,3 +1,4 @@ +# shellcheck shell=sh verify_checksum() { file=$1 expected_checksum=$2 diff --git a/scripts/jinja/templates/partials/utils/download.sh b/scripts/jinja/templates/partials/utils/download.sh index bc37697a..46768b72 100644 --- a/scripts/jinja/templates/partials/utils/download.sh +++ b/scripts/jinja/templates/partials/utils/download.sh @@ -1,6 +1,8 @@ +# shellcheck shell=sh INSTALLED_VERSION=$(dpkg-query -W -f='${Version}' "$AGENT_DEB_PKG_NAME" 2>/dev/null || echo "") -# replace '~' with '-' +# replace '~' with '-' if [ -n "$INSTALLED_VERSION" ]; then + # shellcheck disable=SC2001 # POSIX sh lacks ${var//search/replace}; sed is required INSTALLED_VERSION=$(echo "$INSTALLED_VERSION" | sed 's/~/-/g') fi diff --git a/scripts/jinja/templates/partials/utils/parse-from-pkg.sh b/scripts/jinja/templates/partials/utils/parse-from-pkg.sh index 70703a9c..baed9a44 100644 --- a/scripts/jinja/templates/partials/utils/parse-from-pkg.sh +++ b/scripts/jinja/templates/partials/utils/parse-from-pkg.sh @@ -1,3 +1,4 @@ +# shellcheck shell=sh if [ -n "$FROM_PKG" ]; then log "Installing from package on local machine: '$FROM_PKG'" if [ ! -f "$FROM_PKG" ]; then @@ -12,7 +13,9 @@ if [ -n "$FROM_PKG" ]; then if [ "$(dpkg -f "$FROM_PKG" Architecture)" != "$DEB_ARCH" ]; then fatal "The provided package architecture ($(dpkg -f "$FROM_PKG" Architecture)) does not match this machine's architecture ($DEB_ARCH)." fi + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script AGENT_DEB_PKG=$FROM_PKG + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script VERSION=$(dpkg -f "$FROM_PKG" Version) fi \ No newline at end of file diff --git a/scripts/jinja/templates/partials/utils/provision.sh b/scripts/jinja/templates/partials/utils/provision.sh index 9543811b..da0fd017 100644 --- a/scripts/jinja/templates/partials/utils/provision.sh +++ b/scripts/jinja/templates/partials/utils/provision.sh @@ -1,8 +1,10 @@ +# shellcheck shell=sh if [ -z "$MIRU_API_KEY" ]; then echo "MIRU_API_KEY is not set" exit 1 fi +# shellcheck disable=SC2153 # DEVICE_NAME is an external environment variable response_body=$(curl --request POST \ --url "$BACKEND_HOST"/v1/devices \ --header 'Content-Type: application/json' \ @@ -72,6 +74,7 @@ response_body=$(echo "$response_body" | head -n -1) # Check if the request succeeded if [ "$http_code" -eq 200 ] || [ "$http_code" -eq 201 ]; then log "Successfully created activation token" + # shellcheck disable=SC2034 # consumed by a later partial in the rendered script MIRU_ACTIVATION_TOKEN=$(echo "$response_body" | jq -r '.token') else error "Activation token request failed (HTTP status $http_code)" diff --git a/scripts/jinja/templates/partials/utils/version.sh b/scripts/jinja/templates/partials/utils/version.sh index 71b408f0..46d7eecc 100644 --- a/scripts/jinja/templates/partials/utils/version.sh +++ b/scripts/jinja/templates/partials/utils/version.sh @@ -1,3 +1,4 @@ +# shellcheck shell=sh if [ -z "$VERSION" ]; then if [ "$PRERELEASE" = true ]; then log "Fetching latest pre-release version..." diff --git a/scripts/lint-surface.sh b/scripts/lint-surface.sh index 50491d64..dc7521ae 100755 --- a/scripts/lint-surface.sh +++ b/scripts/lint-surface.sh @@ -15,18 +15,14 @@ echo "" echo "shellcheck" echo "----------" -# Exclude: -# - the .agents subtree (content owned by another repo); -# - the jinja template fragments under scripts/jinja/templates/ (shebang-less -# .sh partials concatenated by scripts/jinja/render.sh; shellcheck flags -# them SC2148 on their own); -# - the generated install scripts under scripts/install/ (rendered from the -# jinja templates by scripts/jinja/render.py — they are build artifacts, so -# findings must be fixed in the templates, not the generated output). +# Exclude only the .agents subtree (content owned by another repo). Every other +# .sh file — including the shebang-less jinja partials under +# scripts/jinja/templates/ and the generated install scripts under +# scripts/install/ — is linted here, mirroring the shared surface-lint workflow. +# Findings in the generated scripts must still be fixed in the jinja templates, +# not the rendered output. find . -name '*.sh' \ ! -path './.agents/*' \ - ! -path './scripts/jinja/templates/*' \ - ! -path './scripts/install/*' \ -exec shellcheck {} + echo "" From 08dc465a8054175c32da032f65bc7673d2373761 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 19:11:05 +0000 Subject: [PATCH 6/7] fix(lint): skip surface lint in scripts/lint.sh under CI The Rust lint CI job runs scripts/lint.sh, which now invokes scripts/lint-surface.sh. Surface linting is already enforced in CI by the dedicated surface-lint job (shared reusable workflow) that installs its own yamllint/shellcheck/actionlint toolchain, so requiring those tools in the Rust lint job is redundant and was failing the job. Guard the call with [ "$CI" != "true" ] so it still runs for local developer invocations, mirroring the existing CI guard in scripts/lib/install-lint-deps.sh. --- scripts/lint.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/lint.sh b/scripts/lint.sh index 2f8f3f22..2a0e5290 100755 --- a/scripts/lint.sh +++ b/scripts/lint.sh @@ -12,4 +12,11 @@ export RUN_DIET="1" "$REPO_ROOT/scripts/lib/lint.sh" -"$REPO_ROOT/scripts/lint-surface.sh" +# Surface lint (YAML / shell / GitHub Actions) is enforced in CI by a dedicated +# job (.github/workflows/ci.yml: surface-lint, via the shared reusable +# workflow), which installs its own toolchain. Run it here only for local +# developer invocations so the Rust lint CI job need not install the surface +# tools. Mirrors the CI guard in scripts/lib/install-lint-deps.sh. +if [ "$CI" != "true" ]; then + "$REPO_ROOT/scripts/lint-surface.sh" +fi From c3d1fdd4a5500df286b09e14ed076de1c9943e19 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 3 Jun 2026 19:14:30 +0000 Subject: [PATCH 7/7] style(install): silence SC2329 on the trap-invoked cleanup() shellcheck 0.11.x (used by the CI surface-lint action) reports the trap-invoked cleanup() function as SC2329 "function never invoked", whereas 0.9.x reports the equivalent SC2317 on its body. The directive only disabled SC2317, so CI still failed. Disable both codes so the suppression is robust across shellcheck versions, and regenerate the install scripts. --- scripts/install/install.sh | 4 ++-- scripts/install/provision.sh | 4 ++-- scripts/install/staging-install.sh | 4 ++-- scripts/install/staging-provision.sh | 4 ++-- scripts/install/uat-install.sh | 4 ++-- scripts/install/uat-provision.sh | 4 ++-- scripts/jinja/templates/partials/utils/activate.sh | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/scripts/install/install.sh b/scripts/install/install.sh index b3c508d0..a365672a 100755 --- a/scripts/install/install.sh +++ b/scripts/install/install.sh @@ -3,7 +3,7 @@ set -e # Script: install.sh # Jinja Template: install.j2 -# Build Timestamp: 2026-06-03T19:03:43.199612 +# Build Timestamp: 2026-06-03T19:14:13.912548 # Description: Install the Miru Agent # DISPLAY # @@ -265,7 +265,7 @@ fi # ACTIVATE THE AGENT # # ------------------ # # shellcheck shell=sh -# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap +# shellcheck disable=SC2317,SC2329 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? diff --git a/scripts/install/provision.sh b/scripts/install/provision.sh index 0da6e879..cd1cbba9 100755 --- a/scripts/install/provision.sh +++ b/scripts/install/provision.sh @@ -3,7 +3,7 @@ set -e # Script: provision.sh # Jinja Template: provision.j2 -# Build Timestamp: 2026-06-03T19:03:43.199612 +# Build Timestamp: 2026-06-03T19:14:13.912548 # Description: Provision a device & install the Miru Agent # DISPLAY # @@ -365,7 +365,7 @@ fi # ACTIVATE THE AGENT # # ------------------ # # shellcheck shell=sh -# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap +# shellcheck disable=SC2317,SC2329 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? diff --git a/scripts/install/staging-install.sh b/scripts/install/staging-install.sh index 5fb04d5b..182110c5 100755 --- a/scripts/install/staging-install.sh +++ b/scripts/install/staging-install.sh @@ -3,7 +3,7 @@ set -e # Script: staging-install.sh # Jinja Template: install.j2 -# Build Timestamp: 2026-06-03T19:03:43.199612 +# Build Timestamp: 2026-06-03T19:14:13.912548 # Description: Install the Miru Agent in the staging environment # DISPLAY # @@ -265,7 +265,7 @@ fi # ACTIVATE THE AGENT # # ------------------ # # shellcheck shell=sh -# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap +# shellcheck disable=SC2317,SC2329 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? diff --git a/scripts/install/staging-provision.sh b/scripts/install/staging-provision.sh index 52489b0b..05c89c94 100755 --- a/scripts/install/staging-provision.sh +++ b/scripts/install/staging-provision.sh @@ -3,7 +3,7 @@ set -e # Script: staging-provision.sh # Jinja Template: provision.j2 -# Build Timestamp: 2026-06-03T19:03:43.199612 +# Build Timestamp: 2026-06-03T19:14:13.912548 # Description: Provision a device & install the Miru Agent in the staging environment # DISPLAY # @@ -365,7 +365,7 @@ fi # ACTIVATE THE AGENT # # ------------------ # # shellcheck shell=sh -# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap +# shellcheck disable=SC2317,SC2329 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? diff --git a/scripts/install/uat-install.sh b/scripts/install/uat-install.sh index 13f14eb1..478c179b 100755 --- a/scripts/install/uat-install.sh +++ b/scripts/install/uat-install.sh @@ -3,7 +3,7 @@ set -e # Script: uat-install.sh # Jinja Template: install.j2 -# Build Timestamp: 2026-06-03T19:03:43.199612 +# Build Timestamp: 2026-06-03T19:14:13.912548 # Description: Install the Miru Agent in the UAT environment # DISPLAY # @@ -265,7 +265,7 @@ fi # ACTIVATE THE AGENT # # ------------------ # # shellcheck shell=sh -# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap +# shellcheck disable=SC2317,SC2329 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? diff --git a/scripts/install/uat-provision.sh b/scripts/install/uat-provision.sh index a0024c18..d8a96e31 100755 --- a/scripts/install/uat-provision.sh +++ b/scripts/install/uat-provision.sh @@ -3,7 +3,7 @@ set -e # Script: uat-provision.sh # Jinja Template: provision.j2 -# Build Timestamp: 2026-06-03T19:03:43.199612 +# Build Timestamp: 2026-06-03T19:14:13.912548 # Description: Provision a device & install the Miru Agent in the UAT environment # DISPLAY # @@ -365,7 +365,7 @@ fi # ACTIVATE THE AGENT # # ------------------ # # shellcheck shell=sh -# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap +# shellcheck disable=SC2317,SC2329 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$? diff --git a/scripts/jinja/templates/partials/utils/activate.sh b/scripts/jinja/templates/partials/utils/activate.sh index b3ca2b19..5cfdebfb 100644 --- a/scripts/jinja/templates/partials/utils/activate.sh +++ b/scripts/jinja/templates/partials/utils/activate.sh @@ -1,5 +1,5 @@ # shellcheck shell=sh -# shellcheck disable=SC2317 # cleanup() is invoked indirectly via trap +# shellcheck disable=SC2317,SC2329 # cleanup() is invoked indirectly via trap cleanup() { exit_code=$?