diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4874fe81..c4f9f054 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -23,6 +23,9 @@ jobs: - name: Checkout code uses: actions/checkout@v6 + - name: Run shellcheck + run: make lint-shell + - name: Set up Go uses: actions/setup-go@v6 with: diff --git a/Makefile b/Makefile index 031b104b..7b268589 100644 --- a/Makefile +++ b/Makefile @@ -144,6 +144,10 @@ lint: golangci-lint ## Run golangci-lint linter lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes $(GOLANGCI_LINT) run --fix +.PHONY: lint-shell +lint-shell: ## Run shellcheck on shell scripts + find . -name '*.sh' -not -path './.git/*' -not -path './vendor/*' -print0 | xargs -0 shellcheck --severity=warning + ##@ Build .PHONY: build diff --git a/hack/create-release-branch.sh b/hack/create-release-branch.sh index 8abbebf2..763f2158 100755 --- a/hack/create-release-branch.sh +++ b/hack/create-release-branch.sh @@ -65,7 +65,7 @@ if [[ ! "$VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then fi # Parse version components -IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION" +IFS='.' read -r MAJOR MINOR _PATCH <<< "$VERSION" RELEASE_BRANCH="release-${MAJOR}.${MINOR}.x" log_info "Creating release branch for version $VERSION" diff --git a/hack/deploy-catalog.sh b/hack/deploy-catalog.sh index abe24417..85bb9520 100755 --- a/hack/deploy-catalog.sh +++ b/hack/deploy-catalog.sh @@ -125,7 +125,7 @@ if [ -z "$INTERNAL_REGISTRY" ]; then oc patch configs.imageregistry.operator.openshift.io/cluster --patch '{"spec":{"defaultRoute":true}}' --type=merge echo "Waiting for registry route to be created..." - for i in {1..30}; do + for _ in {1..30}; do INTERNAL_REGISTRY=$(oc get route default-route -n openshift-image-registry -o jsonpath='{.spec.host}' 2>/dev/null || echo "") if [ -n "$INTERNAL_REGISTRY" ]; then break @@ -268,12 +268,12 @@ fi echo "" echo "Ensuring push permissions..." -oc policy add-role-to-user system:image-pusher $(oc whoami) -n ${NAMESPACE} 2>/dev/null || true -oc policy add-role-to-user system:image-pusher $(oc whoami) -n ${CATALOG_NAMESPACE} 2>/dev/null || true +oc policy add-role-to-user system:image-pusher "$(oc whoami)" -n ${NAMESPACE} 2>/dev/null || true +oc policy add-role-to-user system:image-pusher "$(oc whoami)" -n ${CATALOG_NAMESPACE} 2>/dev/null || true echo "" echo "Logging in to OpenShift registry..." -${CONTAINER_TOOL} login -u $(oc whoami) -p $(oc whoami -t) ${REGISTRY} --tls-verify=false +${CONTAINER_TOOL} login -u "$(oc whoami)" -p "$(oc whoami -t)" ${REGISTRY} --tls-verify=false echo "" echo "Ensuring namespace ${NAMESPACE} exists..." @@ -285,7 +285,7 @@ CLUSTER_ARCHS=$(oc get nodes -o jsonpath='{.items[*].status.nodeInfo.architectur echo "Found architectures: ${CLUSTER_ARCHS}" # Build multi-arch manifest if cluster has multiple architectures -ARCHS_ARRAY=(${CLUSTER_ARCHS}) +read -ra ARCHS_ARRAY <<< "${CLUSTER_ARCHS}" if [ ${#ARCHS_ARRAY[@]} -gt 1 ]; then echo "" echo "Multi-arch cluster detected. Building for all architectures..." @@ -434,7 +434,7 @@ if [ "$COMMAND" = "redeploy" ]; then echo "" echo "Waiting for catalog pod to be ready..." - for i in {1..60}; do + for _ in {1..60}; do CATALOG_POD=$(oc get pods -n ${CATALOG_NAMESPACE} -l olm.catalogSource=${CATALOG_NAME} -o name 2>/dev/null || echo "") if [ -n "$CATALOG_POD" ]; then oc wait --for=condition=Ready ${CATALOG_POD} -n ${CATALOG_NAMESPACE} --timeout=120s && break @@ -452,7 +452,7 @@ if [ "$COMMAND" = "redeploy" ]; then echo "" echo "Waiting for CSV to be installed..." - for i in {1..60}; do + for _ in {1..60}; do CSV=$(oc get csv -n ${NAMESPACE} -o name 2>/dev/null | grep automotive-dev-operator || echo "") if [ -n "$CSV" ]; then PHASE=$(oc get ${CSV} -n ${NAMESPACE} -o jsonpath='{.status.phase}' 2>/dev/null || echo "") @@ -473,7 +473,7 @@ if [ "$COMMAND" = "redeploy" ]; then echo "" echo "Waiting for operator deployment to be available..." - for i in {1..30}; do + for _ in {1..30}; do if oc get deployment ado-operator -n ${NAMESPACE} &>/dev/null; then oc wait --for=condition=Available deployment/ado-operator -n ${NAMESPACE} --timeout=300s && break fi diff --git a/hack/rebuild-catalog.sh b/hack/rebuild-catalog.sh index 7cda9927..4bd61073 100755 --- a/hack/rebuild-catalog.sh +++ b/hack/rebuild-catalog.sh @@ -25,7 +25,7 @@ echo "==========================================" echo "" echo "Logging in to OpenShift registry..." -${CONTAINER_TOOL} login -u $(oc whoami) -p $(oc whoami -t) ${REGISTRY} --tls-verify=false +${CONTAINER_TOOL} login -u "$(oc whoami)" -p "$(oc whoami -t)" ${REGISTRY} --tls-verify=false echo "" echo "Regenerating catalog..." diff --git a/internal/common/tasks/scripts/build_builder.sh b/internal/common/tasks/scripts/build_builder.sh index 0976cba9..675e1896 100644 --- a/internal/common/tasks/scripts/build_builder.sh +++ b/internal/common/tasks/scripts/build_builder.sh @@ -1,3 +1,4 @@ +# shellcheck shell=bash # NOTE: common.sh is prepended to this script at embed time. echo "Prepare builder for distro: $DISTRO, arch: $TARGET_ARCH" diff --git a/internal/common/tasks/scripts/build_image.sh b/internal/common/tasks/scripts/build_image.sh index ccf5fe37..18019c21 100644 --- a/internal/common/tasks/scripts/build_image.sh +++ b/internal/common/tasks/scripts/build_image.sh @@ -1,3 +1,4 @@ +# shellcheck shell=bash # NOTE: common.sh is prepended to this script at embed time. # Initialize optimizations @@ -154,7 +155,7 @@ fi install_custom_ca_certs setup_osbuild -cd "$WORKSPACE_PATH" +cd "$WORKSPACE_PATH" || exit EXPORT_FORMAT="$(params.export-format)" # If format is empty, AIB defaults to raw @@ -647,7 +648,7 @@ AIB_IMAGE_PINNED=$(cat /tmp/aib-pinned.txt 2>/dev/null || echo "$AIB_IMAGE_REF") echo "AIB image pinned reference: $AIB_IMAGE_PINNED" echo -n "${AIB_IMAGE_PINNED}" > /tekton/results/automotive-image-builder -pushd /output +pushd /output || exit mkdir -p "$WORKSPACE_PATH" # Check if disk image was created (only exists when BUILD_DISK_IMAGE=true or non-bootc mode) @@ -778,7 +779,7 @@ elif [ -d "$WORKSPACE_PATH/${exportFile}" ]; then parts_dir="$WORKSPACE_PATH/${final_compressed_name}-parts" mkdir -p "$parts_dir" ( - cd "$WORKSPACE_PATH" + cd "$WORKSPACE_PATH" || exit for item in "${exportFile}"/*; do [ -e "$item" ] || continue base=$(basename "$item") @@ -802,11 +803,11 @@ elif [ -d "$WORKSPACE_PATH/${exportFile}" ]; then echo "Compressed archive size:" && ls -lah "$WORKSPACE_PATH/${final_compressed_name}" || true if [ -f "$WORKSPACE_PATH/${final_compressed_name}" ]; then echo "Removing uncompressed directory ${exportFile} (keeping parts directory)" - rm -rf "$WORKSPACE_PATH/${exportFile}" - pushd "$WORKSPACE_PATH" - ln -sf ${final_compressed_name} disk.img + rm -rf "${WORKSPACE_PATH:?}/${exportFile:?}" + pushd "$WORKSPACE_PATH" || exit + ln -sf "${final_compressed_name}" disk.img final_name="${final_compressed_name}" - popd + popd || exit echo "Available artifacts:" ls -la "$WORKSPACE_PATH/" || true if [ -d "$WORKSPACE_PATH/${final_compressed_name}-parts" ]; then @@ -819,10 +820,10 @@ elif [ -f "${DISK_IMAGE_SOURCE}/${exportFile}" ]; then compress_file "${DISK_IMAGE_SOURCE}/${exportFile}" "$WORKSPACE_PATH/${exportFile}${EXT_FILE}" || { echo "Error: Failed to compress ${exportFile}"; exit 1; } echo "Compressed file size:" && ls -lah "$WORKSPACE_PATH/${exportFile}${EXT_FILE}" || true if [ -f "$WORKSPACE_PATH/${exportFile}${EXT_FILE}" ]; then - pushd "$WORKSPACE_PATH" - ln -sf ${exportFile}${EXT_FILE} disk.img + pushd "$WORKSPACE_PATH" || exit + ln -sf "${exportFile}${EXT_FILE}" disk.img final_name="${exportFile}${EXT_FILE}" - popd + popd || exit fi fi diff --git a/internal/common/tasks/scripts/common.sh b/internal/common/tasks/scripts/common.sh index 85b69d9b..94a8930b 100644 --- a/internal/common/tasks/scripts/common.sh +++ b/internal/common/tasks/scripts/common.sh @@ -98,6 +98,7 @@ setup_cluster_auth() { echo "DEBUG: Reading service account token" TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) echo "DEBUG: Reading service account namespace" + # shellcheck disable=SC2034 # NAMESPACE is used by scripts prepended with common.sh NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace) REGISTRY="${1:-$INTERNAL_REGISTRY}" echo "DEBUG: Using registry: $REGISTRY" @@ -281,6 +282,7 @@ detect_stat_command() { declare -g GET_SIZE_CMD="stat -f%z" echo "DEBUG: Using BSD stat" else + # shellcheck disable=SC2034 # GET_SIZE_CMD is used by scripts prepended with common.sh declare -g GET_SIZE_CMD="echo ''" echo "DEBUG: No working stat command found" fi diff --git a/internal/common/tasks/scripts/find_manifest.sh b/internal/common/tasks/scripts/find_manifest.sh index b0d0a5f9..3ed54e1b 100644 --- a/internal/common/tasks/scripts/find_manifest.sh +++ b/internal/common/tasks/scripts/find_manifest.sh @@ -4,9 +4,9 @@ set -e echo "looking for manifest file..." echo "listing contents of manifest config workspace:" -ls -la $(workspaces.manifest-config-workspace.path) +ls -la "$(workspaces.manifest-config-workspace.path)" -MANIFEST_FILE=$(find $(workspaces.manifest-config-workspace.path) -name '*.mpp.yml' -o -name '*.aib.yml' -type f | head -n 1) +MANIFEST_FILE=$(find "$(workspaces.manifest-config-workspace.path)" \( -name '*.mpp.yml' -o -name '*.aib.yml' \) -type f | head -n 1) if [ -z "$MANIFEST_FILE" ]; then echo "No manifest file found in the ConfigMap" @@ -74,4 +74,4 @@ echo "updated manifest contents:" cat "$workspace_manifest" mkdir -p /tekton/results -echo -n "$workspace_manifest" > /tekton/results/manifest-file-path +printf '%s' "$workspace_manifest" > /tekton/results/manifest-file-path diff --git a/internal/common/tasks/scripts/flash_image.sh b/internal/common/tasks/scripts/flash_image.sh index 4ca4cf55..52082c65 100644 --- a/internal/common/tasks/scripts/flash_image.sh +++ b/internal/common/tasks/scripts/flash_image.sh @@ -1,3 +1,4 @@ +# shellcheck shell=bash # NOTE: common.sh is prepended to this script at embed time. set -uo pipefail diff --git a/internal/common/tasks/scripts/push_artifact.sh b/internal/common/tasks/scripts/push_artifact.sh index 30dac304..20e8ad1a 100644 --- a/internal/common/tasks/scripts/push_artifact.sh +++ b/internal/common/tasks/scripts/push_artifact.sh @@ -1,3 +1,4 @@ +# shellcheck shell=bash # NOTE: common.sh is prepended to this script at embed time. ORAS_VERSION="1.2.0" @@ -205,7 +206,7 @@ else echo "No partition configuration found, skipping default-partitions annotation" fi -cd /workspace/shared +cd /workspace/shared || exit # Verify artifact integrity against the digest produced by the build task. EXPECTED_DIGEST="$(params.expected-artifact-digest)" @@ -257,7 +258,7 @@ if [ -d "${parts_dir}" ] && [ -n "$(ls -A "${parts_dir}" 2>/dev/null)" ]; then ls -la "${parts_dir}/" - cd "${parts_dir}" + cd "${parts_dir}" || exit # Create annotations file in current directory (ORAS container may not have /tmp) annotations_file="./oras-annotations.json"