diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index ffc759a..137af1a 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -269,12 +269,18 @@ jobs: set -e echo "status=${status}" >> "${GITHUB_OUTPUT}" - - name: Finalize Zsh Syntax Result + - name: Run Zsh Regression Tests + id: zsh_tests + if: steps.zsh_syntax.outputs.status == '0' + run: zsh tests/installomator-label-parser.zsh + + - name: Finalize Zsh Validation Result if: success() || failure() shell: bash run: | target_count="${{ steps.collect_targets.outputs.count }}" syntax_status="${{ steps.zsh_syntax.outputs.status }}" + tests_outcome="${{ steps.zsh_tests.outcome }}" if [[ "${target_count}" == "0" ]]; then { @@ -286,19 +292,20 @@ jobs: fi { - echo "### Zsh Syntax" + echo "### Zsh Validation" echo echo "- Files scanned: ${target_count}" + echo "- Regression tests: ${tests_outcome}" } >> "${GITHUB_STEP_SUMMARY}" - if [[ "${syntax_status}" == "0" ]]; then + if [[ "${syntax_status}" == "0" && "${tests_outcome}" == "success" ]]; then echo "" >> "${GITHUB_STEP_SUMMARY}" - echo "✅ All zsh files passed \`zsh -n\`" >> "${GITHUB_STEP_SUMMARY}" + echo "✅ Zsh syntax and regression tests passed" >> "${GITHUB_STEP_SUMMARY}" exit 0 fi echo "" >> "${GITHUB_STEP_SUMMARY}" - echo "❌ zsh syntax errors found" >> "${GITHUB_STEP_SUMMARY}" + echo "❌ Zsh validation failed" >> "${GITHUB_STEP_SUMMARY}" exit 1 shellcheck: diff --git a/CHANGELOG.md b/CHANGELOG.md index b2b9a9b..68b90cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,8 @@ All notable changes to this project are documented in this file. -## 1.2.0b2 - 18-Aug-2026 +## 1.2.0b3 - 18-Aug-2026 +- Fixed validation of Installomator labels declared in multiline alias arms (Bug Report #16) - Added `selectionDialogDefaultChecked` to configure default selection for interactive-mode items (while keeping already-installed items disabled and unchecked; thanks for FR #14, @jeffmw777!) - Updated `codex` Validation Path diff --git a/README.md b/README.md index a0a8c0e..b7b583c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![GitHub release (latest by date)](https://img.shields.io/github/v/release/Setup-Your-Mac/SYM-Lite?display_name=tag) ![GitHub issues](https://img.shields.io/github/issues-raw/Setup-Your-Mac/SYM-Lite) ![GitHub closed issues](https://img.shields.io/github/issues-closed-raw/Setup-Your-Mac/SYM-Lite) ![GitHub pull requests](https://img.shields.io/github/issues-pr-raw/Setup-Your-Mac/SYM-Lite) ![GitHub closed pull requests](https://img.shields.io/github/issues-pr-closed-raw/Setup-Your-Mac/SYM-Lite) [![swiftDialog](https://img.shields.io/badge/swiftDialog-Enabled-blue)](https://swiftdialog.app) [![Semgrep Security Scan](https://img.shields.io/badge/security%20scanned%20by-Semgrep-00C7B7?style=flat&logo=semgrep&logoColor=white)](https://semgrep.dev) -# SYM-Lite (1.2.0b2) +# SYM-Lite (1.2.0b3) > **SYM-Lite** is a lean, purpose-built script for executing MDM-agnostic [Installomator labels](https://github.com/Installomator/Installomator/tree/main/fragments/labels) and [Homebrew](https://brew.sh) casks / formulas, as well as Jamf Pro-specific [policy triggers](https://learn.jamf.com/r/en-US/jamf-pro-documentation-current/Triggers_for_Policies), all through a unified [swiftDialog](https://swiftdialog.app) selection and reporting interface. @@ -71,7 +71,7 @@ installomatorLabels=( ) ``` -At runtime, SYM-Lite validates each configured label against `organizationInstallomatorFile` before building the picker or accepting silent-mode CSV input. If a label is missing from that Installomator file, or if the Installomator file is unavailable or unreadable, SYM-Lite logs a warning or error and removes Installomator labels from the current run while leaving other item types available. +At runtime, SYM-Lite validates each configured label against single-line and continued top-level alias arms in `organizationInstallomatorFile` before building the picker or accepting silent-mode CSV input. If a label is missing from that Installomator file, or if the Installomator file is unavailable, unreadable, or cannot be parsed, SYM-Lite logs a warning or error and removes Installomator labels from the current run while leaving other item types available. ### Adding Homebrew Items @@ -341,6 +341,6 @@ swiftDialog's [Inspect Mode](https://swiftdialog.app/advanced/inspect-mode/) use (The rest of the document — Logging, Troubleshooting, Testing Checklist, Next Steps, and Support — remains unchanged as the reordering was already applied where relevant.) -**Version:** 1.2.0b2 +**Version:** 1.2.0b3 **Date:** 18-Aug-2026 **Author:** Dan K. Snelson (@dan-snelson) diff --git a/SYM-Lite.zsh b/SYM-Lite.zsh index 9462d24..033fb0c 100644 --- a/SYM-Lite.zsh +++ b/SYM-Lite.zsh @@ -16,7 +16,8 @@ # # HISTORY # -# Version 1.2.0b2, 18-Aug-2026, Dan K. Snelson (@dan-snelson) +# Version 1.2.0b3, 18-Aug-2026, Dan K. Snelson (@dan-snelson) +# - Fixed validation of Installomator labels declared in multiline alias arms (Bug Report #16) # - Added `selectionDialogDefaultChecked` to configure default selection for interactive-mode items (while keeping already-installed items disabled and unchecked; thanks for FR #14, @jeffmw777!) # - Updated `codex` Validation Path # @@ -35,7 +36,7 @@ setopt NONOMATCH setopt TYPESET_SILENT # Script Version -scriptVersion="1.2.0b2" +scriptVersion="1.2.0b3" # Script Human-readable Name humanReadableScriptName="Setup Your Mac Lite: Developer Edition" @@ -516,6 +517,7 @@ function getAvailableInstallomatorLabels() { BEGIN { inLabelCase = 0 caseDepth = 0 + continuedArm = "" } /^[[:space:]]*case[[:space:]]+\$label[[:space:]]+in[[:space:]]*$/ { @@ -525,6 +527,31 @@ function getAvailableInstallomatorLabels() { } inLabelCase { + if (continuedArm != "") { + if (caseDepth != 1) { + exit 2 + } + + if ($0 ~ /^[[:space:]]*[A-Za-z0-9_*][A-Za-z0-9_|-]*\|\\[[:space:]]*$/) { + labelFragment = $0 + sub(/^[[:space:]]*/, "", labelFragment) + sub(/\\[[:space:]]*$/, "", labelFragment) + continuedArm = continuedArm labelFragment + next + } + + if ($0 ~ /^[[:space:]]*[A-Za-z0-9_*][A-Za-z0-9_|-]*\)[[:space:]]*$/) { + labelFragment = $0 + sub(/^[[:space:]]*/, "", labelFragment) + sub(/\)[[:space:]]*$/, "", labelFragment) + print continuedArm labelFragment + continuedArm = "" + next + } + + exit 2 + } + if ($0 ~ /^[[:space:]]*case[[:space:]].*[[:space:]]+in[[:space:]]*$/) { caseDepth++ next @@ -538,6 +565,13 @@ function getAvailableInstallomatorLabels() { next } + if (caseDepth == 1 && $0 ~ /^[[:space:]]*[A-Za-z0-9_*][A-Za-z0-9_|-]*\|\\[[:space:]]*$/) { + continuedArm = $0 + sub(/^[[:space:]]*/, "", continuedArm) + sub(/\\[[:space:]]*$/, "", continuedArm) + next + } + if (caseDepth == 1 && $0 ~ /^[[:space:]]*[A-Za-z0-9_*][A-Za-z0-9_|-]*\)[[:space:]]*$/) { labelArm = $0 sub(/^[[:space:]]*/, "", labelArm) @@ -545,6 +579,12 @@ function getAvailableInstallomatorLabels() { print labelArm } } + + END { + if (continuedArm != "") { + exit 2 + } + } ' "${organizationInstallomatorFile}"); then return 1 fi